package handlers_test

import (
	"fmt"
	"net/http"
	"testing"

	"otaupdate/handlers"
	"otaupdate/services"

	"github.com/fiskerinc/cloud-services/pkg/cache"
	m "github.com/fiskerinc/cloud-services/pkg/common"
	dbtc "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
	htc "github.com/fiskerinc/cloud-services/pkg/httpclient/tester"
	"github.com/fiskerinc/cloud-services/pkg/mongo"
	"github.com/fiskerinc/cloud-services/pkg/redis"
	rtc "github.com/fiskerinc/cloud-services/pkg/redis/tester"
	th "github.com/fiskerinc/cloud-services/pkg/testhelper"
	tr "github.com/fiskerinc/cloud-services/pkg/testrunner"
	"github.com/fiskerinc/cloud-services/pkg/utils/elptr"
)

var schemaToTRex = "file://" + th.GetSchemaDirPath() + "/trex/RXMessage.json"

func TestVehicleUpdate(t *testing.T) {
	client, err := services.GetMongoClient()
	if err != nil {
		t.Error(err)
		return
	}
	mockMongo := mongo.NewVehiclesCollection(&mongo.MockCollection{})
	client.SetVehicles(mockMongo)
	mockRedis := rtc.MockRedis{}
	services.SetRedisClientPool(rtc.NewMockClientPool(&mockRedis))
	mockDB := dbtc.MockCars{}
	services.GetDB().SetCars(&mockDB)
	vin := "1G1FP87S3GN100062"
	trexKey := m.TRex.Key(vin)
	cacheKey := redis.CarConfigKey(vin)

	tests := []tr.TestCase{
		{
			Name: "Good data",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPut, "http://example.com/vehicle", m.UpdateCarRequest{
					VIN:        vin,
					Model:      "Ocean",
					Year:       2021,
					Trim:       "Basic",
					Country:    "Germany",
					Powertrain: "test pt",
					Restraint:  "US Spec",
					BodyType:   "test bt",
					CANBus: &m.CANBus{
						Enabled:    true,
						DataLogger: false,
						// DTCEnabled: elptr.ElPtr(true),
					},
					IDPSEnabled: true,
				}),
				ExpectedStatus:   http.StatusOK,
				ExpectedResponse: `{"vin":"1G1FP87S3GN100062","year":2021,"model":"Ocean","trim":"Basic","country":"Germany","powertrain":"test pt","restraint":"US Spec","body_type":"test bt","canbus":{"enabled":true,"data_logger_enabled":false,"dtc_enabled":true},"idps_enabled":true}`,
			},
			RedisTestCase: &rtc.RedisTestCase{
				ExpectedMessages: map[string]string{
					trexKey: `{"data":{"canbus":{"data_logger_enabled":false,"dtc_enabled":true,"enabled":true},"log_level":"trace"},"handler":"config"}`,
				},
				ExpectedCaches: map[string]rtc.ExpiringCacheResult{
					cacheKey: {Value: "DELETED"},
				},
			},
		},
		{
			Name: "Good data with debug mask",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPut, "http://example.com/vehicle", m.UpdateCarRequest{
					VIN:        vin,
					Model:      "Ocean",
					Year:       2021,
					Trim:       "Basic",
					Country:    "Germany",
					Powertrain: "test pt",
					Restraint:  "US Spec",
					BodyType:   "test bt",
					CANBus: &m.CANBus{
						Enabled:    true,
						DataLogger: false,
						DTCEnabled: elptr.ElPtr(false),
					},
					DebugMask:   "E",
					IDPSEnabled: true,
				}),
				ExpectedStatus:   http.StatusOK,
				ExpectedResponse: `{"vin":"1G1FP87S3GN100062","year":2021,"model":"Ocean","trim":"Basic","country":"Germany","powertrain":"test pt","restraint":"US Spec","body_type":"test bt","canbus":{"enabled":true,"data_logger_enabled":false,"dtc_enabled":false},"idps_enabled":true,"debug_mask":"E"}`,
			},
			RedisTestCase: &rtc.RedisTestCase{
				ExpectedMessages: map[string]string{
					trexKey: `{"data":{"canbus":{"data_logger_enabled":false,"dtc_enabled":false,"enabled":true},"debug_mask":"E","log_level":"trace"},"handler":"config"}`,
				},
				ExpectedCaches: map[string]rtc.ExpiringCacheResult{
					cacheKey: {Value: "DELETED"},
				},
			},
		},
		{
			Name: "No CANBus",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPut, "http://example.com/vehicle", m.UpdateCarRequest{
					VIN:         vin,
					Model:       "Ocean",
					Year:        2021,
					Trim:        "Basic",
					IDPSEnabled: true,
				}),
				ExpectedStatus:   http.StatusOK,
				ExpectedResponse: `{"vin":"1G1FP87S3GN100062","year":2021,"model":"Ocean","trim":"Basic","idps_enabled":true}`,
			},
			RedisTestCase: &rtc.RedisTestCase{
				ExpectedMessages: map[string]string{},
				ExpectedCaches: map[string]rtc.ExpiringCacheResult{
					cacheKey: {Value: "DELETED"},
				},
			},
		},
		{
			Name: "No data",
			HttpTestCase: &htc.HttpTestCase{
				Request:          th.MakeTestRequest(http.MethodPut, "http://example.com/vehicle", nil),
				ExpectedStatus:   http.StatusBadRequest,
				ExpectedResponse: `{"message":"VIN required. Year required. Model required. Trim required","error":"Bad Request"}`,
			},
		},
		{
			Name: "No VIN",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPut, "http://example.com/vehicle", m.Car{
					Model: "Ocean",
					Year:  2021,
					Trim:  "Basic",
				}),
				ExpectedStatus:   http.StatusBadRequest,
				ExpectedResponse: `{"message":"VIN required","error":"Bad Request"}`,
			},
		},
		{
			Name: "Bad data",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPut, "http://example.com/vehicle", m.Car{
					VIN:   "XXXXXXXXXXXXXXXXXXXXX",
					Model: "Ocean",
					Year:  2021,
					Trim:  "Basic",
				}),
				ExpectedStatus:   http.StatusBadRequest,
				ExpectedResponse: `{"message":"VIN 'XXXXXXXXXXXXXXXXXXXXX' invalid","error":"Bad Request"}`,
			},
		},
		{
			Name: "Error",
			HttpTestCase: &htc.HttpTestCase{
				Request: th.MakeTestRequest(http.MethodPut, "http://example.com/vehicle", m.Car{
					VIN:   "1G1FP87S3GN100062",
					Model: "Ocean",
					Year:  2021,
					Trim:  "Basic",
				}),
				ExpectedStatus:   http.StatusServiceUnavailable,
				ExpectedResponse: `{"message":"something went wrong","error":"Service Unavailable"}`,
			},
			DBTestCase: &dbtc.DBTestCase{
				MockError: fmt.Errorf("something went wrong"),
			},
		},
	}

	schemaTesterTRex := th.NewSchemaTestHelper(t, schemaToTRex)
	for _, test := range tests {
		mockRedis.Reset()

		if test.DBTestCase != nil {
			test.DBTestCase.SetupDB(&mockDB)
		}
		if test.RedisTestCase != nil {
			test.RedisTestCase.SetupRedis(&mockRedis)
		}

		if test.HttpTestCase != nil {
			w := test.HttpTestCase.Test(handlers.HandleVehicleUpdate)
			test.HttpTestCase.ValidateHttp(t, test.Name, w)
		}
		if test.DBTestCase != nil {
			test.DBTestCase.Validate(t, test.Name, &mockDB)
		}
		if test.RedisTestCase != nil {
			test.RedisTestCase.Validate(t, test.Name, &mockRedis)
			// By default in all environments, ENABLE_DEBUG_MASK is true
			// so we set that, otherwise unit tests fail.
			t.Setenv(cache.ENABLE_DBG_MASK_EV_NAME, cache.ENABLE_DBG_MASK_VAL_TRUE)
			cache.ENABLE_DEBUG_MASK = cache.DbgMaskEnabled()
			for _, mes := range test.RedisTestCase.ExpectedMessages {
				schemaTesterTRex.ValidateSchemaObject(test.Name, []byte(mes))
			}
		}
	}
}
