package handlers_test

import (
	"encoding/json"
	"fmt"
	"testing"
	"time"

	"github.com/pkg/errors"

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

	"github.com/fiskerinc/cloud-services/pkg/common"
	q "github.com/fiskerinc/cloud-services/pkg/db/queries"
	"github.com/fiskerinc/cloud-services/pkg/db/queries/mocks"
	"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
	"github.com/fiskerinc/cloud-services/pkg/redis/tester"
	vconfig "github.com/fiskerinc/cloud-services/pkg/vehicleconfig"
	"github.com/stretchr/testify/assert"
)

var (
	someErr    = errors.New("some error")
	vinMock    = "FISKER123"
	vodMock    = "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"
	confMock   = "000000000000000000000000000000000000000"
	validOrder = common.VehicleOrder{
		SpecID:            800010200,
		OrderNumber:       8000102,
		MessageIdentifier: "VEHICLEORDERSUBMISSION",
		VehicleSpecification: common.VehicleSpecification{
			OrderIndicator:           "S",
			FleetOrderIndicator:      "N",
			ProductionPhaseIndicator: "01",
			VehicleIndicator:         "000",
			ManufacturingPlant:       "G",
			ExpectedReferenceDate: common.ExpectedReferenceDate{
				Time: time.Date(2022, 5, 26, 0, 0, 0, 0, time.UTC),
			},
			ModelType:          "FM29",
			ModelYearIndicator: 2023,
			VehicleModel:       "F29",
			VinPrefix:          "VCF1ZBU2_PG",
			VehicleFeatures: []common.FeatureCodes{
				{
					FamilyCode:  "2801",
					FeatureCode: "280102",
				},
				{
					FamilyCode:  "2804",
					FeatureCode: "280401",
				},
				{
					FamilyCode:  "2805",
					FeatureCode: "280501",
				},
			},
		},
	}
	validUpdateManifest = common.UpdateManifest{
		ID:           1,
		CarUpdateID:  1,
		Version:      fmt.Sprint(validOrder.OrderNumber),
		Description:  fmt.Sprintf("configuration %s %s", vinMock, validOrder.MessageIdentifier),
		ManifestType: common.ConfigUpdateType,
		VOD:          "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
	}
	validRespECUs = []*common.UpdateManifestECU{
		{
			UpdateManifestID: 1,
			ECU:              "ICC",
			Configuration:    confMock,
		},
	}
	smsMock  = sms.NewSMSMockSuccess()
	mockCars = mocks.MockCars{}
)

func TestOrderUpdated(t *testing.T) {
	handlers.ENABLE_ORDERUPDATE_SENDS = true
	validUpdateManifestResp := validUpdateManifest
	validUpdateManifestResp.ID = 1
	validUpdateManifestResp.ECUs = validRespECUs

	scrubbedValidUpdateManifest := validUpdateManifestResp.ToUpdateConfigManifest()
	scrubbedValidUpdateManifest.Type = "standard"

	successPayload, _ := json.Marshal(validOrder)
	redisMock := tester.NewRedisMock()
	services.SetRedisClientPool(tester.NewMockClientPool(redisMock))
	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",
				},
				{
					FamilyCode:  "VOD",
					FeatureCode: vodMock,
				},
			}}, nil
	}}
	services.SetSapService(mockSap)
	mockCars.SetLoadResp(&common.Car{
		VIN:   vinMock,
		ICCID: "1234567890",
		SoldStatus: common.CarSoldStatusRetailed,
	})

	tests := map[string]struct {
		updateManifest q.UpdateManifestsInterface
		cars           q.CarsInterface
		carsUpdate     q.CarUpdatesInterface
		ratePlan       q.RatePlanInterface
		vConfig        vconfig.ConfigServiceInterface
		payload        []byte
		expRedisMsgs   map[string]interface{}
		expErr         error
	}{
		"success": {
			updateManifest: &mocks.MockUpdateManifests{SelectResponse: []common.UpdateManifest{{ID: 1234}}},
			cars:           &mockCars,
			carsUpdate:     &mocks.MockCarUpdates{},
			ratePlan:       &mocks.MockRatePlan{},
			vConfig:        vconfig.ConfigMock{GetVODCDSCodingDataMock: SuccessGetCDSMock},
			payload:        successPayload,
			expRedisMsgs: map[string]interface{}{
				"2:" + vinMock: common.Message{
					Handler: "car_update",
					Data: common.CarUpdate{
						ID:               1,
						VIN:              vinMock,
						UpdateManifestID: 1,
						UpdateManifest:   &validUpdateManifestResp,
					},
				},
				"1:" + vinMock: common.Message{
					Handler: "config_update",
					Data:    scrubbedValidUpdateManifest,
				},
			},
			expErr: nil,
		},
		"json_parse_err": {
			payload: []byte(`12`),
			expErr:  errors.New("json: cannot unmarshal number into Go value of type common.VehicleOrder"),
		},
		"failed_cars_db": {
			updateManifest: &mocks.MockUpdateManifests{SelectResponse: []common.UpdateManifest{{ID: 1234}}},
			cars: &mocks.MockCars{
				DBMockHelper: mocks.DBMockHelper{
					Error: someErr,
				},
			},
			carsUpdate: &mocks.MockCarUpdates{},
			ratePlan:   &mocks.MockRatePlan{},
			vConfig:    vconfig.ConfigMock{GetVODCDSCodingDataMock: SuccessGetCDSMock},
			payload:    successPayload,
			expErr:     someErr,
		},
		"failed_cds": {
			updateManifest: &mocks.MockUpdateManifests{SelectResponse: []common.UpdateManifest{{ID: 1234}}},
			carsUpdate:     &mocks.MockCarUpdates{},
			cars:           &mockCars,
			ratePlan:       &mocks.MockRatePlan{},
			vConfig:        vconfig.ConfigMock{GetVODCDSCodingDataMock: FailedGetCDSMock},
			payload:        successPayload,
			expErr:         someErr,
		},
		"failed_db": {
			updateManifest: &mocks.MockUpdateManifests{
				DBMockHelper: mocks.DBMockHelper{
					Error: someErr,
				},
			},
			cars:       &mockCars,
			carsUpdate: &mocks.MockCarUpdates{},
			ratePlan:   &mocks.MockRatePlan{},
			vConfig:    vconfig.ConfigMock{GetVODCDSCodingDataMock: SuccessGetCDSMock},
			payload:    successPayload,
			expErr:     someErr,
		},
	}

	for tname, tt := range tests {
		t.Run(tname, func(t *testing.T) {
			redisMock.Reset()
			db := services.GetDB()
			db.SetManifests(tt.updateManifest)
			db.SetCars(tt.cars)
			db.SetCarUpdates(tt.carsUpdate)
			db.SetRatePlan(tt.ratePlan)
			services.SetVehicleConfig(tt.vConfig)
			err := handlers.OrderUpdated(db, &smsMock, vinMock, tt.payload)
			if err != nil && tt.expErr != nil {
				assert.Equal(t, tt.expErr.Error(), err.Error())
				return
			}
			assert.Equal(t, tt.expErr, err)
			// assert.Equal(t, tt.expRedisMsgs, redisMock.PublishedMessages)
		})
	}
}

func SuccessGetCDSMock(request common.VODCDSRequest) (map[string]string, error) {
	ecus := map[string]string{
		"VOD": vodMock,
		"ICC": confMock,
	}

	return ecus, nil
}
func FailedGetCDSMock(request common.VODCDSRequest) (map[string]string, error) {
	return nil, someErr
}
