package handlers_test

import (
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/fiskerinc/cloud-services/services/attendant/handlers"
	"github.com/fiskerinc/cloud-services/services/attendant/services"

	"os"

	"github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/common/dbbasemodel"
	"github.com/fiskerinc/cloud-services/pkg/common/manifestfingerprintparams"
	dbm "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
	"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
	"github.com/fiskerinc/cloud-services/pkg/kafka"
	kafkaMock "github.com/fiskerinc/cloud-services/pkg/kafka/mock"
	"github.com/fiskerinc/cloud-services/pkg/redis"
	rm "github.com/fiskerinc/cloud-services/pkg/redis/tester"
	th "github.com/fiskerinc/cloud-services/pkg/testhelper"
	"github.com/fiskerinc/cloud-services/pkg/testrunner"
	"github.com/fiskerinc/cloud-services/pkg/utils/elptr"
	vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
	"github.com/jinzhu/copier"
)

func TestSendManifest(t *testing.T) {
	os.Setenv("APP_SERVICE_NAME", "ATTENDANT")
	handlers.PrivateSeed = 123456789

	ka := services.NewKeepAwakeService()
	ka.SetService(&services.MockKeepAwakeImplementation{})

	now := time.Now()
	testVIN := "JH4KA7680RC01"
	tboxKey := common.TRex.Key(testVIN)
	hmiKey := common.HMI.Key(testVIN)

	smsKey := "manifest_tbox_send_cache:100"

	var bhex common.BinaryHex = []byte("test")
	redis.MockRedisConnection()
	falsePtrValue := elptr.ElPtr(false)
	truePtrValue := elptr.ElPtr(true)
	ecuaRollback := []*common.UpdateManifestECU{
		{
			ID:               100,
			UpdateManifestID: 200,
			ECU:              "ECUA",
			Version:          "VERSIONOLD",
			Mode:             "A",
			DBModelBase:      th.Timestamp,
			Files: []*common.UpdateManifestFile{
				{
					FileID:              "FILEIDOLD",
					UpdateManifestECUID: 1001,
					Filename:            "FILENAMEOLD",
					URL:                 "URLOLD",
					FileType:            common.Software,
					WriteRegionID:       700,
					WriteRegion: common.MemoryRegion{
						Offset: 701,
						Length: 702,
					},
					FileSize:    240,
					DBModelBase: th.Timestamp,
				},
			},
		},
	}
	ecus := []*common.UpdateManifestECU{
		{
			ECU:             "ICC",
			Version:         "SWVERSION",
			HWVersions:      []string{"HWVERSION"},
			Mode:            "D",
			SelfDownload:    true,
			InstallPriority: 13,
			Files: []*common.UpdateManifestFile{
				{
					FileID:        "AAAAAAA",
					URL:           "http://download.com/file1.bin",
					FileSize:      1000,
					Checksum:      "AAAAAAA",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Calibration,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					DBModelBase: th.Timestamp,
				},
				{
					FileID:        "SHOULD_NOT_BE_IN_UPDATE",
					URL:           "http://download.com/SHOULD_NOT_BE_IN_UPDATE.bin",
					FileSize:      1000,
					Checksum:      "AAAAAAA",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Calibration,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					Parsed:      falsePtrValue,
					DBModelBase: th.Timestamp,
				},
				{
					FileID:        "MUST_BE_IN_UPDATE",
					URL:           "http://download.com/MUST_BE_IN_UPDATE.bin",
					FileSize:      1000,
					Checksum:      "AAAAAAA",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Calibration,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					Parsed:      truePtrValue,
					DBModelBase: th.Timestamp,
				},
			},
		},
		{
			ECU:             "ECUD",
			Version:         "SWVERSION",
			HWVersions:      []string{"HWVERSION"},
			Mode:            "D",
			InstallPriority: 4,
			Files: []*common.UpdateManifestFile{
				{
					FileID:        "SHOULD_NOT_BE_IN_UPDATE",
					URL:           "http://download.com/SHOULD_NOT_BE_IN_UPDATE.bin",
					FileSize:      1000,
					Checksum:      "AAAAAAA",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Calibration,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					Parsed:      falsePtrValue,
					DBModelBase: th.Timestamp,
				},
				{
					FileID:        "MUST_BE_IN_UPDATE",
					URL:           "http://download.com/MUST_BE_IN_UPDATE.bin",
					FileSize:      1000,
					Checksum:      "AAAAAAA",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Calibration,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					Parsed:      truePtrValue,
					DBModelBase: th.Timestamp,
				},
				{
					FileID:        "BBBBBBB",
					URL:           "http://download.com/file2.bin",
					FileSize:      2000,
					Checksum:      "BBBBBBB",
					FileType:      common.Calibration,
					WriteRegionID: 300,
					WriteRegion: common.MemoryRegion{
						Offset: 301,
						Length: 302,
					},
					EraseRegionID: 400,
					EraseRegion: &common.MemoryRegion{
						Offset: 401,
						Length: 402,
					},
					DBModelBase: th.Timestamp,
				},
			},
			DBModelBase: th.Timestamp,
		},
		{
			ID:                100,
			UpdateManifestID:  200,
			ECU:               "PDU",
			Version:           "PDU-VERS",
			HWVersions:        []string{"PDU-VERS"},
			Mode:              "PDU",
			ConfigurationMask: "AAAAAAAA",
			InstallPriority:   7,
			Files: []*common.UpdateManifestFile{
				{
					FileID:        "NOT_BE_IN_UPDATE",
					URL:           "http://download.com/NOT_BE_IN_UPDATE.bin",
					FileSize:      1000,
					Checksum:      "AAAAAAA",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Calibration,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					Parsed:      falsePtrValue,
					DBModelBase: th.Timestamp,
				},
				{
					FileID:        "MUST_BE_IN_UPDATE",
					URL:           "http://download.com/MUST_BE_IN_UPDATE.bin",
					FileSize:      1000,
					Checksum:      "AAAAAAA",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Calibration,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					Parsed:      truePtrValue,
					DBModelBase: th.Timestamp,
				},
				{
					FileID:        "AAAAAAAA",
					URL:           "http://download.com/filea.bin",
					FileSize:      2000,
					Checksum:      "AAAAAAAA",
					WriteRegionID: 500,
					FileType:      common.Calibration,
					WriteRegion: common.MemoryRegion{
						Offset: 501,
						Length: 502,
					},
					DBModelBase: th.Timestamp,
				},
			},
			DBModelBase: th.Timestamp,
		},
		{
			ID:                100,
			UpdateManifestID:  200,
			ECU:               "ECUA",
			Version:           "A-VERS",
			HWVersions:        []string{"A-VERS"},
			Mode:              "A",
			ConfigurationMask: "AAAAAAAA",
			InstallPriority:   1,
			Files: []*common.UpdateManifestFile{
				{
					FileID:              "FILEID",
					UpdateManifestECUID: 100,
					Filename:            "FILENAME",
					URL:                 "URL",
					FileType:            common.Calibration,
					WriteRegionID:       600,
					WriteRegion: common.MemoryRegion{
						Offset: 601,
						Length: 602,
					},
					FileSize:    240,
					DBModelBase: th.Timestamp,
				},
			},
			DBModelBase: th.Timestamp,
		},
	}

	fingerprint := getTodaysFingerprint()

	standardManifest := common.UpdateManifest{
		ID:              100,
		CarUpdateID:     297,
		Name:            "TEST",
		Version:         "MANIFEST_VERSION",
		SUMS:            "2023.10.01.00",
		Description:     "TESTDESC",
		ReleaseNotes:    "http://fiskerinc.com/release_notes",
		Type:            common.ManifestTypeForced,
		Active:          truePtrValue,
		Country:         "US",
		PowerTrain:      "MD23",
		Restraint:       "None",
		Model:           "Ocean",
		Trim:            "Sport",
		Year:            2022,
		BodyType:        "truck",
		RollbackEnabled: true,
		ECUs:            ecus,
		UpdateDuration:  30,
	}

	rollbackDisabledManifest := common.UpdateManifest{
		ID:             100,
		CarUpdateID:    297,
		Name:           "TEST",
		Version:        "MANIFEST_VERSION",
		SUMS:           "2023.10.01.00.E",
		Description:    "TESTDESC",
		ReleaseNotes:   "http://fiskerinc.com/release_notes",
		Type:           "standard",
		Active:         truePtrValue,
		Country:        "US",
		PowerTrain:     "MD23",
		Restraint:      "None",
		Model:          "Ocean",
		Trim:           "Sport",
		Year:           2022,
		BodyType:       "truck",
		ECUs:           ecus,
		UpdateDuration: 30,
	}
	standardManifestNoICC := common.UpdateManifest{
		ID:              100,
		CarUpdateID:     297,
		Name:            "TEST",
		Version:         "MANIFEST_VERSION",
		SUMS:            "2023.10.01.00.E",
		Description:     "TESTDESC",
		ReleaseNotes:    "http://fiskerinc.com/release_notes",
		Type:            "standard",
		Country:         "US",
		PowerTrain:      "MD23",
		Restraint:       "None",
		Model:           "Ocean",
		Trim:            "Sport",
		Year:            2022,
		RollbackEnabled: true,
		BodyType:        "truck",
		ECUs: []*common.UpdateManifestECU{
			{
				ECU:             "ECUD",
				HWVersions:      []string{"HWVERSION"},
				Version:         "SWVERSION",
				Mode:            "D",
				InstallPriority: 7,
				Files: []*common.UpdateManifestFile{
					{
						FileID:        "BBBBBBB",
						URL:           "http://download.com/file2.bin",
						FileSize:      2000,
						Checksum:      "BBBBBBB",
						FileType:      common.Calibration,
						WriteRegionID: 700,
						WriteRegion: common.MemoryRegion{
							Offset: 701,
							Length: 702,
						},
						DBModelBase: th.Timestamp,
					},
				},
				DBModelBase: th.Timestamp,
			},
			{
				ID:                100,
				UpdateManifestID:  200,
				ECU:               "PDU",
				Version:           "PDU-VERS",
				HWVersions:        []string{"PDU-VERS"},
				Mode:              "PDU",
				ConfigurationMask: "AAAAAAAA",
				InstallPriority:   10,
				Files: []*common.UpdateManifestFile{
					{
						FileID:        "AAAAAAAA",
						URL:           "http://download.com/filea.bin",
						FileSize:      2000,
						FileType:      common.Calibration,
						Checksum:      "AAAAAAAA",
						WriteRegionID: 800,
						WriteRegion: common.MemoryRegion{
							Offset: 801,
							Length: 802,
						},
						EraseRegionID: 900,
						EraseRegion: &common.MemoryRegion{
							Offset: 901,
							Length: 902,
						},
						DBModelBase: th.Timestamp,
					},
				},
				DBModelBase: th.Timestamp,
			},
			{
				ID:                100,
				UpdateManifestID:  200,
				ECU:               "ECUA",
				Version:           "A-VERS",
				HWVersions:        []string{"A-VERS"},
				Mode:              "A",
				ConfigurationMask: "AAAAAAAA",
				InstallPriority:   4,
				Files: []*common.UpdateManifestFile{
					{
						FileID:              "FILEID",
						UpdateManifestECUID: 100,
						Filename:            "FILENAME",
						URL:                 "URL",
						FileType:            common.Calibration,
						WriteRegionID:       600,
						WriteRegion: common.MemoryRegion{
							Offset: 601,
							Length: 602,
						},
						FileSize:    240,
						DBModelBase: th.Timestamp,
					},
				},
				DBModelBase: th.Timestamp,
			},
		},
		UpdateDuration: 30,
	}
	mockCarUpdate := &common.CarUpdate{
		ID:               297,
		VIN:              testVIN,
		UpdateManifestID: 100,
	}
	mockCarECUs := []common.CarECU{
		{ECU: "ECUD", HWVersion: "HWVERSION"},
		{ECU: "ECUA", HWVersion: "A-VERS", Version: "TEST122"},
		{ECU: "OBC", HWVersion: "PDU-VERS", Version: "TEST121"},
		{ECU: "BCM", HWVersion: ";#A;#"},
		{ECU: "ICC", HWVersion: "HWVERSION", Version: "TEST123"},
	}
	mockRedis := rm.MockRedis{}
	mockCUDB := dbm.MockCarUpdates{}
	mockManDB := dbm.MockUpdateManifests{}
	mockKeys := dbm.MockEccKeys{
		MockListResponse: []common.ECCKeys{
			{
				ECU:      "PDU",
				PrivKey1: &bhex,
				PrivKey2: &bhex,
				PrivKey3: &bhex,
				PubKey1:  &bhex,
				PubKey2:  &bhex,
				PubKey3:  &bhex,
				Env:      "current",
				DBModelBase: dbbasemodel.DBModelBase{
					CreatedAt: &now,
					UpdatedAt: &now,
				},
			},
			{
				ECU:      "ECUA",
				PrivKey1: &bhex,
				PrivKey2: &bhex,
				PrivKey3: &bhex,
				PubKey1:  &bhex,
				PubKey2:  &bhex,
				PubKey3:  &bhex,
				Env:      "current",
				DBModelBase: dbbasemodel.DBModelBase{
					CreatedAt: &now,
					UpdatedAt: &now,
				},
			},
			{
				ECU:      "ECUD",
				PrivKey1: &bhex,
				PrivKey2: &bhex,
				PrivKey3: &bhex,
				PubKey1:  &bhex,
				PubKey2:  &bhex,
				PubKey3:  &bhex,
				Env:      "current",
				DBModelBase: dbbasemodel.DBModelBase{
					CreatedAt: &now,
					UpdatedAt: &now,
				},
			},
		},
	}
	services.GetDB().SetCarUpdates(&mockCUDB)
	services.GetDB().SetCars(&dbm.MockCars{SelectCarECUs: mockCarECUs, SelectResponse: &common.Car{ICCID: "8000000000000000000"}})
	services.GetDB().SetManifests(&mockManDB)
	services.GetDB().SetECCKeys(&mockKeys)
	services.SetRedisClientPool(rm.NewMockClientPool(&mockRedis))
	mockSap := vconfig.SAPServiceMock{GetConfigurationMock: func(vin string) (common.SAPResponse, error) {
		return common.SAPResponse{
			ModelYear:              2023,
			ModelType:              "Ocean",
			VersionDuringModelYear: "1",
			Features: []common.SAPFeature{
				{
					FamilyCode:  "FamilyCode1",
					FeatureCode: "FeatureCode1",
				},
				{
					FamilyCode:  "FamilyCode2",
					FeatureCode: "FeatureCode2",
				},
			},
		}, nil
	}}
	mockConf := vconfig.ConfigMock{GetVODCDSCodingDataMock: func(request common.VODCDSRequest) (map[string]string, error) {
		return map[string]string{
			"EPS":  "000147303031313632011D",
			"ESP":  "000F47303031313632000001012301027600000101000002B0",
			"ECUA": "config",
			"VOD":  "",
			"ADAS": "000147303031313632011D",
		}, nil
	}}
	mockSMS := &sms.SMSMock{}
	mockSMS.SetHandleSMSQueueResponse(&sms.SMSQueueResponse{
		SmsMsgID:       "100",
		SentSuccessful: true,
	}, nil)

	services.SetSapService(mockSap)
	services.SetVehicleConfig(mockConf)
	services.SetSmsClient(mockSMS)

	mockKafkaProducer := kafkaMock.GetKafkaMock(nil)
	services.SetKafkaProducer(mockKafkaProducer)

	clonedStandardManifest := common.UpdateManifest{}
	clonedStandardManifestNoICC := common.UpdateManifest{}
	err := copier.CopyWithOption(&clonedStandardManifest, &standardManifest, copier.Option{DeepCopy: true})
	if err != nil {
		t.Fatal(err)
	}
	err = copier.CopyWithOption(&clonedStandardManifestNoICC, &standardManifestNoICC, copier.Option{DeepCopy: true})
	if err != nil {
		t.Fatal(err)
	}

	tests := []testrunner.TestCase{
		{
			Name: "Send manifest w Self Download",
			DBTestCase: &dbm.DBTestCase{
				MockLoadResponse: mockCarUpdate,
				SetupMockResponse: func() {
					mockCUDB.LoadManifest = &clonedStandardManifest
					mockManDB.ECUUpdatesMock = func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) {
						if man.ECU == "ECUA" {
							return ecuaRollback, nil
						}

						return nil, nil
					}
				},
			},
			RedisTestCase: &rm.RedisTestCase{
				Device:      common.Service,
				DeviceKey:   kafka.OTAUpdateService,
				PayloadData: `{"car_update_id":297}`,
				ExpectedMessages: map[string]string{
					hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}]},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":301,"length":302},"erase_region":{"offset":401,"length":402}}]},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":501,"length":502}}]},{"name":"ICC","version":"SWVERSION","current_version":"TEST123","hw_version":"HWVERSION","self_download":true,"files":[{"file_id":"AAAAAAA","url":"http://download.com/file1.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":true,"type":"forced","vod":"01011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110100202310010011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100001e","update_duration":30}}`,
				},
				ExpectedCaches: map[string]rm.ExpiringCacheResult{
					// carupdateKey: {
					// 	Value:   `{"current_size":0,"ecu":"","errorcode":0,"file_size":0,"file_total":0,"id":297,"info":"ICC","installed":0,"status":"sent","total_files":0,"total_size":0}`,
					// 	Expires: expectedExpire,
					// },
					smsKey: {
						Value: `{"VIN":"JH4KA7680RC01","ManifestID":100,"Destination":"ICC"}`,
					},
				},
			},
		},
		{
			Name: "Send manifest w Self Download rollback disabled",
			DBTestCase: &dbm.DBTestCase{
				MockLoadResponse: mockCarUpdate,
				SetupMockResponse: func() {
					mockCUDB.LoadManifest = &rollbackDisabledManifest
					mockManDB.ECUUpdatesMock = func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) {
						if man.ECU == "ECUA" {
							return ecuaRollback, nil
						}

						return nil, nil
					}
				},
			},
			RedisTestCase: &rm.RedisTestCase{
				Device:      common.Service,
				DeviceKey:   kafka.OTAUpdateService,
				PayloadData: `{"car_update_id":297}`,
				ExpectedMessages: map[string]string{
					hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}]},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":301,"length":302},"erase_region":{"offset":401,"length":402}}]},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":501,"length":502}}]},{"name":"ICC","version":"SWVERSION","current_version":"TEST123","hw_version":"HWVERSION","self_download":true,"files":[{"file_id":"AAAAAAA","url":"http://download.com/file1.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"MUST_BE_IN_UPDATE","url":"http://download.com/MUST_BE_IN_UPDATE.bin","file_size":1000,"checksum":"AAAAAAA","type":"calibration","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":false,"type":"standard","vod":"0101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010020231001000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000ba","update_duration":30}}`,
				},
				ExpectedCaches: map[string]rm.ExpiringCacheResult{
					smsKey: {
						Value: `{"VIN":"JH4KA7680RC01","ManifestID":100,"Destination":"ICC"}`,
					},
				},
			},
		},
		{
			Name: "Bad message",
			RedisTestCase: &rm.RedisTestCase{
				Device:        common.HMI,
				DeviceKey:     testVIN,
				PayloadData:   `{"car_update_id":`,
				ExpectedError: "unexpected end of JSON input",
			},
		},
		{
			Name: "Send manifest w No Self Download",
			DBTestCase: &dbm.DBTestCase{
				MockLoadResponse: mockCarUpdate,
				SetupMockResponse: func() {
					mockCUDB.LoadManifest = &clonedStandardManifestNoICC
				},
			},
			RedisTestCase: &rm.RedisTestCase{
				Device:      common.Service,
				DeviceKey:   kafka.OTAUpdateService,
				PayloadData: `{"car_update_id":297}`,
				ExpectedMessages: map[string]string{
					hmiKey:  `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}]},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":701,"length":702}}]},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":801,"length":802},"erase_region":{"offset":901,"length":902}}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":true,"type":"standard","vod":"0101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010020231001000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000ba","update_duration":30}}`,
					tboxKey: `{"handler":"update_manifest","data":{"ecu_updates":[{"name":"ECUA","version":"A-VERS","current_version":"TEST122","hw_version":"A-VERS","configuration_mask":"AAAAAAAA","configuration":"config","files":[{"file_id":"FILEID","url":"URL","file_size":240,"type":"calibration","write_region":{"offset":601,"length":602}}],"rollback":[{"version":"VERSIONOLD","files":[{"file_id":"FILEIDOLD","url":"URLOLD","file_size":240,"type":"software","write_region":{"offset":701,"length":702}}]}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}},{"name":"ECUD","version":"SWVERSION","hw_version":"HWVERSION","files":[{"file_id":"BBBBBBB","url":"http://download.com/file2.bin","file_size":2000,"checksum":"BBBBBBB","type":"calibration","write_region":{"offset":701,"length":702}}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}},{"name":"OBC","version":"PDU-VERS","current_version":"TEST121","hw_version":"PDU-VERS","configuration_mask":"AAAAAAAA","files":[{"file_id":"AAAAAAAA","url":"http://download.com/filea.bin","file_size":2000,"checksum":"AAAAAAAA","type":"calibration","write_region":{"offset":801,"length":802},"erase_region":{"offset":901,"length":902}}],"ecc_keys":{"level_1":"74657374","level_2":"74657374","level_3":"74657374"}}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":true,"type":"standard","vod":"0101111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111010020231001000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110000ba","update_duration":30}}`,
				},
				ExpectedCaches: map[string]rm.ExpiringCacheResult{
					"manifest_tbox_send_cache:UQIDEPFQUH": {
						Value: `{"VIN":"JH4KA7680RC01","ManifestID":0,"Destination":"ICC/TBOX"}`,
					},
				},
			},
		},
	}

	schemaTesterHMI := th.NewSchemaTestHelper(t, schemaToHMI)
	schemaTesterTRex := th.NewSchemaTestHelper(t, schemaToTRex)

	for _, test := range tests {
		mockRedis.Reset()
		for _, ecu := range ecus {
			ecu.Rollback = nil
		}

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

			err := handlers.SendManifest(services.GetDB(), ka, test.RedisTestCase.Device, test.RedisTestCase.DeviceKey, []byte(test.RedisTestCase.PayloadData))

			test.RedisTestCase.CheckHandlerError(t, test.Name, err)
			test.RedisTestCase.Validate(t, test.Name, &mockRedis)
		}

		if test.DBTestCase != nil {
			test.DBTestCase.Validate(t, test.Name, &mockCUDB)
		}

		for key, m := range test.RedisTestCase.ExpectedMessages {
			name := fmt.Sprintf("%s %s", test.Name, key)
			if strings.Contains(key, "2:") {
				schemaTesterHMI.ValidateSchemaObject(name, []byte(m))
				continue
			}

			schemaTesterTRex.ValidateSchemaObject(name, []byte(m))
		}
	}
}

func TestSendManifestMultiFile(t *testing.T) {
	os.Setenv("APP_SERVICE_NAME", "ATTENDANT")
	testVIN := "JH4KA7680RC01"
	now := time.Now()
	hmiKey := common.HMI.Key(testVIN)
	falsePtrValue := elptr.ElPtr(false)
	truePtrValue := elptr.ElPtr(true)
	var bhex common.BinaryHex = []byte("test")
	redis.MockRedisConnection()
	ka := services.NewKeepAwakeService()
	ka.SetService(&services.MockKeepAwakeImplementation{})

	ecus := []*common.UpdateManifestECU{
		{
			ECU:          "BCM",
			Version:      "DB22121A",
			HWVersions:   []string{";#A;#"},
			Mode:         "D",
			SelfDownload: true,
			Files: []*common.UpdateManifestFile{
				{
					FileID:   "ShouldNotSee",
					Filename: "notparsed.s19",
					URL:      "fakeurl.com",
					Checksum: "shouldnotsee",
					Parsed:   falsePtrValue,
				},
				{
					FileID:        "3ee5fd6bc9402d67",
					Filename:      "MAGNA_BCM_FBL_driver.s19_0.bin",
					URL:           "https://upload-dev.fiskerdps.com/4466d78a-50e1-4f1d-a4b8-5f78076758ed/MAGNA_BCM_FBL_driver.s19_0.bin",
					FileSize:      1000,
					Checksum:      "shouldsee",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Bootloader,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					DBModelBase: th.Timestamp,
					Parsed:      truePtrValue,
				},
				{
					FileID:        "7878ede9c9407779",
					Filename:      "FM29_Application_FR40_220111.s19_0.bin",
					URL:           "ttps://upload-dev.fiskerdps.com/b5c3c8c8-86cd-422e-aa11-a248697edfa3/FM29_Application_FR40_220111.s19_0.bin",
					FileSize:      1000,
					Checksum:      "SHOULD NOT SEE",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Software,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					Parsed:      truePtrValue,
					Signature:   "ShouldNOtSee",
					DBModelBase: th.Timestamp,
				},
				{
					FileID:        "7bb5083dc9407ae9",
					Filename:      "FM29_Application_FR40_220111.s19_1.bin",
					URL:           "https://upload-dev.fiskerdps.com/2cdeb566-cb00-42cd-93a8-69769b19269a/FM29_Application_FR40_220111.s19_1.bin",
					FileSize:      1000,
					Checksum:      "shantsee",
					WriteRegionID: 100,
					WriteRegion: common.MemoryRegion{
						Offset: 101,
						Length: 102,
					},
					FileType:      common.Software,
					EraseRegionID: 200,
					EraseRegion: &common.MemoryRegion{
						Offset: 201,
						Length: 202,
					},
					Parsed:      truePtrValue,
					DBModelBase: th.Timestamp,
					Signature:   "SHOULD NOT SEE",
				},
				{
					FileID:    "7ebb4fadc9409b91",
					Filename:  "FM29_Application_FR40_220111.s19_2.bin",
					URL:       "https://upload-dev.fiskerdps.com/ec9f8a84-61fb-4a9b-9735-ac6f91b07a83/FM29_Application_FR40_220111.s19_2.bin",
					FileType:  common.Software,
					Parsed:    truePtrValue,
					Signature: "SomeSignatureYouShouldSee",
				},
			},
		},
	}

	fingerprint := getTodaysFingerprint()

	standardManifest := common.UpdateManifest{
		ID:              100,
		CarUpdateID:     297,
		Name:            "TEST",
		Version:         "MANIFEST_VERSION",
		SUMS:            "2023.10.01.00",
		Description:     "TESTDESC",
		ReleaseNotes:    "http://fiskerinc.com/release_notes",
		Type:            "standard",
		Active:          truePtrValue,
		Country:         "US",
		PowerTrain:      "MD23",
		Restraint:       "None",
		Model:           "Ocean",
		Trim:            "Sport",
		Year:            2022,
		BodyType:        "truck",
		RollbackEnabled: false,
		ECUs:            ecus,
		UpdateDuration:  30,
	}

	mockCarUpdate := &common.CarUpdate{
		ID:               297,
		VIN:              testVIN,
		UpdateManifestID: 100,
	}
	mockCarECUs := []common.CarECU{
		{ECU: "ECUD", HWVersion: "HWVERSION"},
		{ECU: "ECUA", HWVersion: "A-VERS"},
		{ECU: "OBC", HWVersion: "PDU-VERS"},
		{ECU: "BCM", HWVersion: ";#A;#"},
		{ECU: "ICC", HWVersion: "HWVERSION"},
	}
	mockRedis := rm.MockRedis{}
	mockCUDB := dbm.MockCarUpdates{}
	mockManDB := dbm.MockUpdateManifests{}
	mockKeys := dbm.MockEccKeys{
		MockListResponse: []common.ECCKeys{
			{
				ECU:      "BCM",
				PrivKey1: &bhex,
				PrivKey2: &bhex,
				PrivKey3: &bhex,
				PubKey1:  &bhex,
				PubKey2:  &bhex,
				PubKey3:  &bhex,
				Env:      "current",
				DBModelBase: dbbasemodel.DBModelBase{
					CreatedAt: &now,
					UpdatedAt: &now,
				},
			},
		},
	}
	services.GetDB().SetCarUpdates(&mockCUDB)
	services.GetDB().SetCars(&dbm.MockCars{SelectCarECUs: mockCarECUs, SelectResponse: &common.Car{ICCID: ""}})
	services.GetDB().SetManifests(&mockManDB)
	services.GetDB().SetECCKeys(&mockKeys)
	services.SetRedisClientPool(rm.NewMockClientPool(&mockRedis))

	mockSap := vconfig.SAPServiceMock{GetConfigurationMock: func(vin string) (common.SAPResponse, error) {
		return common.SAPResponse{
			ModelYear:              2023,
			ModelType:              "Ocean",
			VersionDuringModelYear: "1",
			Features: []common.SAPFeature{
				{
					FamilyCode:  "FamilyCode1",
					FeatureCode: "FeatureCode1",
				},
				{
					FamilyCode:  "FamilyCode2",
					FeatureCode: "FeatureCode2",
				},
			},
		}, nil
	}}
	mockConf := vconfig.ConfigMock{GetVODCDSCodingDataMock: func(request common.VODCDSRequest) (map[string]string, error) {
		return map[string]string{
			"ECUA": "config",
			"VOD":  "",
		}, nil
	}}
	mockSMS := &sms.SMSMock{}
	mockSMS.SetHandleSMSQueueResponse(&sms.SMSQueueResponse{
		SmsMsgID:       "100",
		SentSuccessful: true,
	}, nil)
	services.SetSmsClient(mockSMS)
	services.SetSapService(mockSap)
	services.SetVehicleConfig(mockConf)
	smsKey := "manifest_tbox_send_cache:100"
	tests := []testrunner.TestCase{
		{
			Name: "MultiFile right order",
			DBTestCase: &dbm.DBTestCase{
				MockLoadResponse: mockCarUpdate,
				SetupMockResponse: func() {
					mockCUDB.LoadManifest = &standardManifest
					mockManDB.ECUUpdatesMock = func(man *common.UpdateManifestECU, vin string) ([]*common.UpdateManifestECU, error) {
						if man.ECU == "ECUA" {
							return nil, nil
						}

						return nil, nil
					}
				},
			},
			RedisTestCase: &rm.RedisTestCase{
				Device:      common.Service,
				DeviceKey:   kafka.OTAUpdateService,
				PayloadData: `{"car_update_id":297}`,
				ExpectedMessages: map[string]string{
					hmiKey: `{"handler":"update_manifest","data":{"name":"TEST","version":"MANIFEST_VERSION","description":"TESTDESC","release_notes":"http://fiskerinc.com/release_notes","ecu_updates":[{"name":"BCM","version":"DB22121A","hw_version":";#A;#","self_download":true,"files":[{"file_id":"3ee5fd6bc9402d67","url":"https://upload-dev.fiskerdps.com/4466d78a-50e1-4f1d-a4b8-5f78076758ed/MAGNA_BCM_FBL_driver.s19_0.bin","file_size":1000,"checksum":"shouldsee","type":"bootloader","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"7878ede9c9407779","url":"ttps://upload-dev.fiskerdps.com/b5c3c8c8-86cd-422e-aa11-a248697edfa3/FM29_Application_FR40_220111.s19_0.bin","file_size":1000,"type":"software","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"7bb5083dc9407ae9","url":"https://upload-dev.fiskerdps.com/2cdeb566-cb00-42cd-93a8-69769b19269a/FM29_Application_FR40_220111.s19_1.bin","file_size":1000,"type":"software","write_region":{"offset":101,"length":102},"erase_region":{"offset":201,"length":202}},{"file_id":"7ebb4fadc9409b91","url":"https://upload-dev.fiskerdps.com/ec9f8a84-61fb-4a9b-9735-ac6f91b07a83/FM29_Application_FR40_220111.s19_2.bin","type":"software","write_region":{"offset":0,"length":0},"signature":"SomeSignatureYouShouldSee"}]}],"fingerprint":"` + fingerprint + `","car_update_id":297,"rollback":false,"type":"standard","vod":"01011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111110100202310010011111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100001e","update_duration":30}}`,
				},
				ExpectedCaches: map[string]rm.ExpiringCacheResult{smsKey: {
					Value: `{"VIN":"JH4KA7680RC01","ManifestID":100,"Destination":"ICC"}`,
				}},
			},
		},
	}

	for _, test := range tests {
		mockRedis.Reset()
		for _, ecu := range ecus {
			ecu.Rollback = nil
		}

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

			err := handlers.SendManifest(services.GetDB(), ka, test.RedisTestCase.Device, test.RedisTestCase.DeviceKey, []byte(test.RedisTestCase.PayloadData))

			test.RedisTestCase.CheckHandlerError(t, test.Name, err)
			test.RedisTestCase.Validate(t, test.Name, &mockRedis)
		}

		if test.DBTestCase != nil {
			test.DBTestCase.Validate(t, test.Name, &mockCUDB)
		}
	}
}

func TestSendManifestIntegration(t *testing.T) {
	os.Setenv("APP_SERVICE_NAME", "ATTENDANT")
	ka := services.NewKeepAwakeService()
	ka.SetService(&services.MockKeepAwakeImplementation{})
	err := handlers.SendManifest(services.GetDB(), ka, common.Service, "", []byte(`{"car_update_id":297}`))
	if err != nil {
		t.Error(err)
	}
}

func getTodaysFingerprint() string {
	// generate today's fingerprint
	fpparams := manifestfingerprintparams.GetFPParams()
	var fm = common.UpdateManifest{}
	fm.GenerateFingerprint(fpparams.CurTime(), fpparams.ManifestSerial())
	return fm.Fingerprint
}
