package tests

import (
	"fmt"
	"github.com/fiskerinc/cloud-services/services/jetfire/services"
	"github.com/fiskerinc/cloud-services/services/jetfire/utils"
	"testing"

	"github.com/fiskerinc/cloud-services/pkg/grpc/kafka_grpc"
	"github.com/stretchr/testify/assert"
)

const testVIN = "TESTVIN1234567890"

var testCanSignalBatch = []kafka_grpc.GRPC_CANSignal{
	{Vin: testVIN, Timestamp: 600.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 100},
	{Vin: testVIN, Timestamp: 601.0, Id: 792, Name: "ESP_VehSpd", Value: 0},
	{Vin: testVIN, Timestamp: 602.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 90},
	{Vin: testVIN, Timestamp: 603.0, Id: 792, Name: "ESP_VehSpd", Value: 10},
	{Vin: testVIN, Timestamp: 604.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 80},
	{Vin: testVIN, Timestamp: 605.0, Id: 792, Name: "ESP_VehSpd", Value: 20},
	{Vin: testVIN, Timestamp: 606.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 80},
	{Vin: testVIN, Timestamp: 607.0, Id: 792, Name: "ESP_VehSpd", Value: 30},
	{Vin: testVIN, Timestamp: 608.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 70},
	{Vin: testVIN, Timestamp: 609.0, Id: 792, Name: "ESP_VehSpd", Value: 40},
	{Vin: testVIN, Timestamp: 608.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 60},
	{Vin: testVIN, Timestamp: 607.0, Id: 792, Name: "ESP_VehSpd", Value: 50},
	{Vin: testVIN, Timestamp: 610.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 50},
	{Vin: testVIN, Timestamp: 611.0, Id: 792, Name: "ESP_VehSpd", Value: 60},
	{Vin: testVIN, Timestamp: 612.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 40},
	{Vin: testVIN, Timestamp: 613.0, Id: 792, Name: "ESP_VehSpd", Value: 70},
	{Vin: testVIN, Timestamp: 614.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 30},
	{Vin: testVIN, Timestamp: 615.0, Id: 792, Name: "ESP_VehSpd", Value: 80},
	{Vin: testVIN, Timestamp: 616.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 20},
	{Vin: testVIN, Timestamp: 617.0, Id: 792, Name: "ESP_VehSpd", Value: 90},
	{Vin: testVIN, Timestamp: 618.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 60},
	{Vin: testVIN, Timestamp: 619.0, Id: 792, Name: "ESP_VehSpd", Value: 100},
	{Vin: testVIN, Timestamp: 650.0, Id: 792, Name: "ESP_VehSpd", Value: 100},

	{Vin: testVIN, Timestamp: 800.0, Id: 792, Name: "ESP_VehSpd", Value: 100},

	{Vin: testVIN, Timestamp: 1500.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 20},
	{Vin: testVIN, Timestamp: 1501.0, Id: 792, Name: "ESP_VehSpd", Value: 90},
	{Vin: testVIN, Timestamp: 1502.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 45},
	{Vin: testVIN, Timestamp: 1503.0, Id: 792, Name: "ESP_VehSpd", Value: 13},

	{Vin: testVIN, Timestamp: 1510.0, Id: 819, Name: "BCM_PwrMod", Value: 0},
	{Vin: testVIN, Timestamp: 1511.0, Id: 819, Name: "BCM_PwrMod", Value: 2},
	{Vin: testVIN, Timestamp: 1512.0, Id: 792, Name: "ESP_VehSpd", Value: 11},
}

var testCanSignalOrderBatch = []kafka_grpc.GRPC_CANSignal{
	{Vin: testVIN, Timestamp: 700.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 100},
	{Vin: testVIN, Timestamp: 601.0, Id: 792, Name: "ESP_VehSpd", Value: 0},
	{Vin: testVIN, Timestamp: 603.0, Id: 792, Name: "ESP_VehSpd", Value: 10},
	{Vin: testVIN, Timestamp: 704.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 80},
	{Vin: testVIN, Timestamp: 605.0, Id: 792, Name: "ESP_VehSpd", Value: 20},
	{Vin: testVIN, Timestamp: 607.0, Id: 792, Name: "ESP_VehSpd", Value: 30},
	{Vin: testVIN, Timestamp: 708.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 70},
	{Vin: testVIN, Timestamp: 609.0, Id: 792, Name: "ESP_VehSpd", Value: 40},
	{Vin: testVIN, Timestamp: 607.0, Id: 792, Name: "ESP_VehSpd", Value: 50},
	{Vin: testVIN, Timestamp: 710.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 50},
	{Vin: testVIN, Timestamp: 611.0, Id: 792, Name: "ESP_VehSpd", Value: 60},
	{Vin: testVIN, Timestamp: 613.0, Id: 792, Name: "ESP_VehSpd", Value: 70},
	{Vin: testVIN, Timestamp: 714.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 30},
	{Vin: testVIN, Timestamp: 615.0, Id: 792, Name: "ESP_VehSpd", Value: 80},
	{Vin: testVIN, Timestamp: 617.0, Id: 792, Name: "ESP_VehSpd", Value: 90},
	{Vin: testVIN, Timestamp: 718.0, Id: 816, Name: "BMS_PwrBattRmngCpSOC", Value: 60},
	{Vin: testVIN, Timestamp: 619.0, Id: 792, Name: "ESP_VehSpd", Value: 100},
}

func TestVehicleCache(t *testing.T) {
	cache := services.GetVehicleCache()

	cache.Clear()

	for i, signal := range testCanSignalBatch {
		cache.UpdateSignal(&signal, 0x0)

		if i == 4 {
			// testing signal aggregation
			state, containsState := cache.Cache[testVIN]
			assert.True(t, containsState)
			assert.NotNil(t, state)

			assert.WithinDuration(t, state.Timestamp, utils.FloatToTime(604.0), 1e8)
			assert.WithinDuration(t, state.TripStart, utils.FloatToTime(600.0), 1e8)
			assert.Equal(t, state.StateValues["BMS_PwrBattRmngCpSOC"], 80.0)
			assert.Equal(t, state.StateValues["ESP_VehSpd"], 10.0)
		}

		if i == 11 {
			// testing out of order messages
			state := cache.Cache[testVIN]
			assert.WithinDuration(t, state.Timestamp, utils.FloatToTime(609.0), 1e8)
			assert.WithinDuration(t, state.TripStart, utils.FloatToTime(600.0), 1e8)
			assert.Equal(t, state.StateValues["BMS_PwrBattRmngCpSOC"], 60.0)
			assert.Equal(t, state.StateValues["ESP_VehSpd"], 40.0)
		}

		if i == 17 {
			// testing signal aggregation after message order is restored
			state := cache.Cache[testVIN]

			assert.WithinDuration(t, state.Timestamp, utils.FloatToTime(615.0), 1e8)
			assert.WithinDuration(t, state.TripStart, utils.FloatToTime(600.0), 1e8)
			assert.Equal(t, state.StateValues["BMS_PwrBattRmngCpSOC"], 30.0)
			assert.Equal(t, state.StateValues["ESP_VehSpd"], 80.0)
		}

		if i == 22 {
			state := cache.Cache[testVIN]

			assert.WithinDuration(t, state.Timestamp, utils.FloatToTime(650.0), 1e8)
			assert.WithinDuration(t, state.TripStart, utils.FloatToTime(600.0), 1e8)
			assert.Equal(t, state.StateValues["BMS_PwrBattRmngCpSOC"], 60.0)
			assert.Equal(t, state.StateValues["ESP_VehSpd"], 100.0)
		}

		if i == 23 {
			state := cache.Cache[testVIN]

			assert.WithinDuration(t, state.Timestamp, utils.FloatToTime(800.0), 1e8)
			assert.WithinDuration(t, state.TripStart, utils.FloatToTime(600.0), 1e8)
			assert.Equal(t, state.StateValues["BMS_PwrBattRmngCpSOC"], 60.0)
			assert.Equal(t, state.StateValues["ESP_VehSpd"], 100.0)
		}

		if i == 25 {
			state := cache.Cache[testVIN]

			assert.WithinDuration(t, state.Timestamp, utils.FloatToTime(1501.0), 1e8)
			assert.WithinDuration(t, state.TripStart, utils.FloatToTime(1500.0), 1e8)
			assert.Equal(t, state.StateValues["BMS_PwrBattRmngCpSOC"], 20.0)
			assert.Equal(t, state.StateValues["ESP_VehSpd"], 90.0)
		}
	}

	assert.Equal(t, len(cache.Cache), 1)

	// testing large timestamp gap; trigger new trip
	state := cache.Cache[testVIN]
	tripStartTime := utils.FloatToTime(1511.0)
	assert.WithinDuration(t, state.TripStart, tripStartTime, 1e8)
	assert.Equal(t, state.TripID, fmt.Sprintf("%s_%d", testVIN, 1511))
	assert.WithinDuration(t, state.Timestamp, utils.FloatToTime(1512.0), 1e8)
	assert.Equal(t, state.StateValues["BMS_PwrBattRmngCpSOC"], 45.0)
	assert.Equal(t, state.StateValues["ESP_VehSpd"], 11.0)
}

func TestVehicleCacheOrderly(t *testing.T) {
	cache := services.GetVehicleCache()

	cache.Clear()

	for i, signal := range testCanSignalOrderBatch {
		cache.UpdateSignal(&signal, 0x0)

		if i == 8 {
			state := cache.Cache[testVIN]

			assert.WithinDuration(t, state.Timestamp, utils.FloatToTime(708.0), 1e8)
			assert.WithinDuration(t, state.TripStart, utils.FloatToTime(700.0), 1e8)
			assert.Equal(t, state.StateValues["BMS_PwrBattRmngCpSOC"], 70.0)
			assert.Equal(t, state.StateValues["ESP_VehSpd"], 40.0)

		}
	}
	state := cache.Cache[testVIN]
	assert.WithinDuration(t, state.Timestamp, utils.FloatToTime(718.0), 1e8)
	assert.WithinDuration(t, state.TripStart, utils.FloatToTime(700.0), 1e8)
	assert.Equal(t, state.StateValues["BMS_PwrBattRmngCpSOC"], 60.0)
	assert.Equal(t, state.StateValues["ESP_VehSpd"], 100.0)

}

func TestVehicleCacheList(t *testing.T) {
	cache := services.GetVehicleCache()

	cache.Clear()

	signal := kafka_grpc.GRPC_CANSignal{
		Vin:       "TESTVIN1",
		Timestamp: 0.0,
		Id:        816,
		Name:      "ESP_VehSpd",
		Value:     792,
	}
	cache.UpdateSignal(&signal, 0x0)

	signal = kafka_grpc.GRPC_CANSignal{
		Vin:       "TESTVIN2",
		Timestamp: 0.0,
		Id:        816,
		Name:      "ESP_VehSpd",
		Value:     792,
	}
	cache.UpdateSignal(&signal, 0x0)

	signal = kafka_grpc.GRPC_CANSignal{
		Vin:       "TESTVIN3",
		Timestamp: 0.0,
		Id:        816,
		Name:      "ESP_VehSpd",
		Value:     792,
	}
	cache.UpdateSignal(&signal, 0x0)

	expectedVins := []string{"TESTVIN1", "TESTVIN2", "TESTVIN3"}
	node := cache.StatesListHead
	i := 0
	for node != nil {
		assert.Equal(t, expectedVins[i], node.VIN)
		node = node.Next
		i++
	}

	node2 := cache.PopLeft()

	expectedVins = []string{"TESTVIN2", "TESTVIN3"}
	node = cache.StatesListHead
	i = 0
	for node != nil {
		assert.Equal(t, expectedVins[i], node.VIN)
		node = node.Next
		i++
	}

	cache.ReinsertRight(node2)

	expectedVins = []string{"TESTVIN2", "TESTVIN3", "TESTVIN1"}
	node = cache.StatesListHead
	i = 0
	for node != nil {
		assert.Equal(t, expectedVins[i], node.VIN)
		node = node.Next
		i++
	}

	cache.ReinsertRight(cache.Cache["TESTVIN3"])

	expectedVins = []string{"TESTVIN2", "TESTVIN1", "TESTVIN3"}
	node = cache.StatesListHead
	i = 0
	for node != nil {
		assert.Equal(t, expectedVins[i], node.VIN)
		node = node.Next
		i++
	}

}
