package utils

import (
	"github.com/fiskerinc/cloud-services/pkg/clickhouse"
	"github.com/fiskerinc/cloud-services/pkg/logger"
)

// NewVehicleMessageFilters constructs a new VehicleMessageFilters
// should only be called once during lifetime of a service
func NewVehicleMessageFilters() *VehicleMessageFilters {
	return &VehicleMessageFilters{
		filters: make(map[string]*MessageFilters),
	}
}

// VehicleMessageFilters maps vehicle vins to their set of filters
type VehicleMessageFilters struct {
	filters map[string]*MessageFilters
}

// NewFiltersForVehicle adds a new vehicle to the sets of filters
func (v *VehicleMessageFilters) NewFiltersForVehicle(client clickhouse.ClientInterface, vin string) *MessageFilters {
	var f *MessageFilters

	d, ok := v.filters[DEFAULT]
	if ok {
		f = NewMessageFiltersFromDefault(d)
	} else {
		f = NewMessageFilters()
	}

	cFilters, err := client.RetrieveFiltersForVehicle(vin)
	if err != nil {
		logger.Warn().Err(err).Send()
	}

	f.PopulateFromClickhouse(cFilters)
	v.filters[vin] = f
	return f
}

// GetFiltersForVehicle obtains filters for a vehicle
func (v *VehicleMessageFilters) GetFiltersForVehicle(client clickhouse.ClientInterface, vin string) *MessageFilters {
	var f *MessageFilters
	var ok bool

	if f, ok = v.filters[vin]; !ok {
		f = v.NewFiltersForVehicle(client, vin)
	}

	return f
}

// SetDefaultFilters sets the default filters for vehicles to use as a base
func (v *VehicleMessageFilters) SetDefaultFilters(filters []clickhouse.CANFilterSchema) {

	d := NewMessageFilters()
	d.PopulateFromClickhouse(filters)
	v.filters[DEFAULT] = d
}

// NewMessageFilters is a convenience function to setup a map of filters
func NewMessageFilters() *MessageFilters {
	return &MessageFilters{
		filters: make(map[int]*MessageFilter),
	}
}

// NewMessageFiltersFromDefault copies default filters to a new struct
func NewMessageFiltersFromDefault(d *MessageFilters) *MessageFilters {
	f := NewMessageFilters()
	for id, key := range d.filters {
		f.SetMessageFilter(id, key.cycle)
	}

	return f
}

// MessageFilters maps CAN IDs to MessageFilter structs
type MessageFilters struct {
	filters map[int]*MessageFilter
}

// SetMessageFilter adds an ID to the filter maps
// cycle expects an integer in ms
func (m *MessageFilters) SetMessageFilter(id int, cycle int) {
	m.filters[id] = &MessageFilter{cycle: cycle}
}

// PopulateFromClickhouse is a helper method to populate
// MessageFilters with filters queried from clickhouse
func (m *MessageFilters) PopulateFromClickhouse(filters []clickhouse.CANFilterSchema) {
	for _, filter := range filters {
		m.SetMessageFilter(int(filter.ID), int(filter.Period)*1000)
	}
}

// AllowMessage verifies if a message can pass through based on timestamp
// if the message passes through calculates the timestamp for the next
// possible message of that ID
// if MessageFilter.Cycle is 0 it means the message should not pass through
// if MessageFilter.Cycle is -1 it means the message should always pass through
// if no filter is present for the ID - it will always pass through (BLACKLIST)
func (m *MessageFilters) AllowMessage(id int, timestamp int) bool {
	filter, ok := m.filters[id]
	if !ok {
		return true
	}

	if filter.cycle == 0 {
		return false
	} else if filter.cycle == -1 {
		return true
	}

	if timestamp > filter.next {
		filter.next = timestamp + filter.cycle
		return true
	}

	return false
}

// MessageFilter keeps track of message cycle rules and the next possible timestamp for a message
// cycle and next are integers in microseconds (CANFilter data that comes in is in microseconds)
type MessageFilter struct {
	cycle, next int
}

const DEFAULT = "DEFAULT"
