package mocks

import (
	"github.com/go-pg/pg/v10/orm"

	"github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/db/queries"
	"github.com/fiskerinc/cloud-services/pkg/validator"
	"github.com/jinzhu/copier"
)

// CarUpdate query methods
type MockCars struct {
	SelectResponse             *common.Car
	SelectC2DResponse          *common.CarToDriver
	SelectCarsResponse         []common.Car
	SelectCarECUs              []common.CarECU
	SelectCarsForDrivers       []common.CarToDriver
	SelectCarsForDriver        common.CarToDriver
	SelectCarSettings          []common.CarSetting
	SelectCarFlashpackVersions []common.CarFlashpackVersion
	DBMockHelper
}

// GetSoftwareVersion implements queries.CarsInterface.
func (c *MockCars) GetSoftwareVersion(vin string) (result common.CarPKCOSVersion, err error) {
	panic("unimplemented")
}

// GetICCIDs implements queries.CarsInterface.
func (c *MockCars) GetICCIDs(vins []string) (iccids []string, err error) {
	panic("unimplemented")
}

// GetWhiteListCars implements queries.CarsInterface.
func (c *MockCars) GetWhiteListCars() (vins []string, err error) {
	panic("unimplemented")
}

// BlacklistCars implements queries.CarsInterface.
func (c *MockCars) BlacklistCars(vin []string) (err error) {
	panic("unimplemented")
}

// WhitelistCars implements queries.CarsInterface.
func (c *MockCars) WhitelistCars(vin []string, source string) (err error) {
	panic("unimplemented")
}

var _ queries.CarsInterface = &MockCars{}

func (c *MockCars) UpdateICCID(car *common.Car) (orm.Result, error) {
	if car.VIN == "" {
		return nil, &validator.FieldError{
			ErrorMsg: "VIN required",
		}
	}

	c.ORMResponse = &MockORMResults{AffectedRows: 1}

	return c.ORMResponse, c.Error
}

func (c *MockCars) UpdateSoldStatus(car *common.Car) (orm.Result, error) {
	if car.VIN == "" {
		return nil, &validator.FieldError{
			ErrorMsg: "VIN required",
		}
	}

	c.ORMResponse = &MockORMResults{AffectedRows: 1}

	return c.ORMResponse, c.Error
}

func (c *MockCars) SelectByID(id int64) (*common.Car, error) {
	return c.SelectResponse, c.Error
}

func (c *MockCars) SelectByVIN(vin string) (*common.Car, error) {
	return c.SelectResponse, c.Error
}

func (c *MockCars) Search(filter *common.CarSearch, paging *queries.PageQueryOptions) ([]common.Car, error) {
	c.LastFilter = filter
	return c.Select(&filter.Car, paging)
}

func (c *MockCars) Select(filter *common.Car, paging *queries.PageQueryOptions) ([]common.Car, error) {
	c.LastPaging = paging

	return c.SelectCarsResponse, c.Error
}

func (c *MockCars) SelectOrInsert(car *common.Car) (bool, error) {
	return c.SelectOrInsertResult, c.Error
}

func (c *MockCars) Delete(car *common.Car) (orm.Result, error) {
	if car.VIN == "" {
		return nil, &validator.FieldError{
			ErrorMsg: "id required",
		}
	}

	return c.ORMResponse, c.Error
}

func (c *MockCars) Update(car *common.Car) (orm.Result, error) {
	if car.VIN == "" {
		return nil, &validator.FieldError{
			ErrorMsg: "VIN required",
		}
	}

	return c.ORMResponse, c.Error
}

func (c *MockCars) CarsByManifest(manifest common.UpdateManifest, paging *queries.PageQueryOptions) ([]common.Car, error) {
	c.LastPaging = paging
	return c.SelectCarsResponse, c.Error
}

func (c *MockCars) CountCarsByManifest(manifest common.UpdateManifest) (int, error) {
	return len(c.SelectCarsResponse), c.Error
}

func (c *MockCars) Insert(car *common.Car) (orm.Result, error) {
	return c.ORMResponse, c.Error
}

func (c *MockCars) Load(car *common.Car) error {
	if c.Error != nil {
		return c.Error
	}

	return nil
}

func (c *MockCars) Count(filter *common.Car) (int, error) {
	return len(c.SelectCarsResponse), c.Error
}

func (c *MockCars) SearchCount(filter *common.CarSearch) (int, error) {
	return len(c.SelectCarsResponse), c.Error
}

func (c *MockCars) AddDriver(car *common.Car, driver *common.Driver, role string) (*common.CarToDriver, error) {
	if c.Error != nil {
		return nil, c.Error
	}
	if c.DriverError != nil {
		return nil, c.DriverError
	}

	return c.SelectC2DResponse, nil
}

func (c *MockCars) SelectCarToDriver(filter *common.CarToDriver) ([]common.CarToDriver, error) {
	if c.Error != nil {
		return nil, c.Error
	}

	return c.SelectCarsForDrivers, nil
}

func (c *MockCars) GetDriver(id string) (common.CarToDriver, error) {
	if c.Error != nil {
		return c.SelectCarsForDriver, c.Error
	}

	return c.SelectCarsForDriver, nil
}

func (c *MockCars) GetDrivers(vin string) ([]common.CarToDriver, error) {
	if c.Error != nil {
		return nil, c.Error
	}

	return c.SelectCarsForDrivers, nil
}

func (c *MockCars) RemoveDriver(vin string, driverID string) (orm.Result, error) {
	if c.Error != nil {
		return c.ORMResponse, c.Error
	}

	return c.ORMResponse, nil
}

func (c *MockCars) GetTRexSetting(vin string) (common.TRexSetting, error) {
	tRexSetting := common.TRexSetting{}
	if c.Error != nil {
		return tRexSetting, c.Error
	}

	return tRexSetting, nil
}

func (c *MockCars) GetModels() ([]string, error) {
	if c.Error != nil {
		return nil, c.Error
	}

	return []string{"1G1FP87S3GN100062"}, nil
}

func (c *MockCars) GetYears() ([]int, error) {
	if c.Error != nil {
		return nil, c.Error
	}

	return []int{3000}, nil
}

func (c *MockCars) SetSetting(setting *common.CarSetting) (orm.Result, error) {
	if c.Error != nil {
		return c.ORMResponse, c.Error
	}

	return c.ORMResponse, nil
}

func (c *MockCars) GetVehicleSpecificSettings(driver *common.CarToDriver) ([]common.CarSetting, error) {
	return c.SelectCarSettings, c.Error
}

func (c *MockCars) GetSettings(driver *common.CarToDriver) ([]common.CarSetting, error) {
	return []common.CarSetting{}, c.Error
}

func (c *MockCars) DeleteSetting(setting *common.CarSetting) (orm.Result, error) {
	return c.ORMResponse, c.Error
}

func (c *MockCars) UpdateCarECU(ecu *common.CarECU) error {
	return c.Error
}

func (c *MockCars) UpdateCarECUs(ecus []common.CarECU) error {
	return c.Error
}

func (c *MockCars) InsertCarECUs(ecus []common.CarECU) error {
	return c.Error
}

func (c *MockCars) uniqueFilter() []common.CarECU {
	seen := make(map[string]bool)
	filteredECUs := make([]common.CarECU, 0)
	for _, ecu := range c.SelectCarECUs {
		if !seen[ecu.VIN+ecu.ECU] {
			filteredECUs = append(filteredECUs, ecu)
			seen[ecu.VIN+ecu.ECU] = true
		}
	}
	return filteredECUs
}

func (c *MockCars) GetCarECUs(filter common.CarECUFilter, paging *queries.PageQueryOptions) ([]common.CarECU, error) {
	copiedList := []common.CarECU{}
	copier.CopyWithOption(&copiedList, &c.SelectCarECUs, copier.Option{DeepCopy: true})
	if filter.Search != "" {
		return []common.CarECU{copiedList[0]}, c.Error
	}

	if filter.Unique {
		return c.uniqueFilter(), c.Error
	}

	return copiedList, c.Error
}

func (c *MockCars) GetCarECUsCount(filter common.CarECUFilter) (int, error) {
	if filter.Search != "" {
		return 1, c.Error
	}

	if filter.Unique {
		return len(c.uniqueFilter()), c.Error
	}

	return len(c.SelectCarECUs), c.Error
}

func (c *MockCars) UpdateCarFlashpackVersion(vin string, flashpack string) (orm.Result, error) {
	return c.ORMResponse, nil
}

func (c *MockCars) GetFlashpackVersions(carModel string, carTrim string, carYear int, options *queries.PageQueryOptions) ([]common.CarFlashpackVersionResponse, error) {
	return []common.CarFlashpackVersionResponse{
		{
			CarModel:  "Ocean",
			CarTrim:   "Base",
			CarYear:   2023,
			Flashpack: "43.19",
		},
		{
			CarModel:  "Ocean",
			CarTrim:   "Base",
			CarYear:   2023,
			Flashpack: "41.14",
		},
	}, nil
}

func (c *MockCars) GetFlashpackVersionsCount(carModel string, carTrim string, carYear int) (int, error) {
	return 2, nil
}

func (c *MockCars) GetNextFlashpackVersion(carModel string, carTrim string, flashpack string) (*common.CarFlashpackVersionResponse, error) {
	return &common.CarFlashpackVersionResponse{
		CarModel:  "Ocean",
		CarTrim:   "Base",
		CarYear:   2023,
		Flashpack: "41.14",
	}, nil
}

func (c *MockCars) GetCarFlashpackVersionMappingsByModelTrim(carModel string, carTrim string, options *queries.PageQueryOptions) ([]common.CarFlashpackVersion, error) {
	if c.SelectCarFlashpackVersions != nil {
		return c.SelectCarFlashpackVersions, nil
	}

	return []common.CarFlashpackVersion{
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "44.14",
			CarECUName:    "ADAS",
			CarECUVersion: "ADASVersion1",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "41.14",
			CarECUName:    "ADAS",
			CarECUVersion: "ADASVersion",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "41.14",
			CarECUName:    "ACUN",
			CarECUVersion: "ACUNVersion",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "39.14",
			CarECUName:    "BCM",
			CarECUVersion: "BCMVersion",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2024,
			Flashpack:     "11.0",
			CarECUName:    "ADAS",
			CarECUVersion: "ADASVersion4",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "39.14",
			CarECUName:    "ADAS",
			CarECUVersion: "ADASVersion0",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "39.14",
			CarECUName:    "ACUN",
			CarECUVersion: "ACUNVersion0",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "39.14",
			CarECUName:    "PDI",
			CarECUVersion: "PDIVersion",
		},
	}, nil
}

func (c *MockCars) GetCarFlashpackVersionMappingsByModelTrimYearFlashpack(carModel string, carTrim string, carYear int, flashpack string, options *queries.PageQueryOptions) ([]common.CarFlashpackVersion, error) {
	if c.SelectCarFlashpackVersions != nil {
		return c.SelectCarFlashpackVersions, nil
	}

	return []common.CarFlashpackVersion{
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "44.14",
			CarECUName:    "ADAS",
			CarECUVersion: "ADASVersion1",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "41.14",
			CarECUName:    "ADAS",
			CarECUVersion: "ADASVersion",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2024,
			Flashpack:     "11.0",
			CarECUName:    "ADAS",
			CarECUVersion: "ADASVersion4",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "41.14",
			CarECUName:    "ACUN",
			CarECUVersion: "ACUNVersion",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "39.14",
			CarECUName:    "BCM",
			CarECUVersion: "BCMVersion",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "39.14",
			CarECUName:    "ADAS",
			CarECUVersion: "ADASVersion0",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "39.14",
			CarECUName:    "ACUN",
			CarECUVersion: "ACUNVersion0",
		},
		{
			CarModel:      "Ocean",
			CarTrim:       "Base",
			CarYear:       2023,
			Flashpack:     "39.14",
			CarECUName:    "PDI",
			CarECUVersion: "PDIVersion",
		},
	}, nil
}

func (c *MockCars) GetCarFlashpackVersionMappingsByModelTrimYearFlashpackCount(carModel string, carTrim string, carYear int, flashpack string) (int, error) {
	if c.SelectCarFlashpackVersions != nil {
		return len(c.SelectCarFlashpackVersions), nil
	}

	return 8, nil
}

func (c *MockCars) GetCarECUNamesByModelTrim(carModel string, carTrim string) ([]string, error) {
	return []string{"ADAS", "ACU", "BMS"}, nil
}

func (c *MockCars) AddCarFlashpackVersionMappings(carFlashpackVersions []common.CarFlashpackVersion) error {
	return nil
}

func (c *MockCars) DeleteFlashpackVersion(carModel string, carTrim string, carYear int, flashpack string) error {
	return nil
}

func (c *MockCars) GetCarsForDriver(driverID string) ([]common.CarToDriver, error) {
	return c.SelectCarsForDrivers, c.Error
}

func (c *MockCars) UpdateBLEKey(vin string, driverid string, blekey string) (common.DriverExternal, error) {
	return common.DriverExternal{}, c.Error
}

func (c *MockCars) ECUUpdatedAt(ecu common.CarECU) (orm.Result, error) {
	return c.ORMResponse, c.Error
}

func (c *MockCars) SetListResp(list interface{}) {
	if list != nil {
		c.SelectCarsResponse = list.([]common.Car)
	} else {
		c.SelectCarsResponse = nil
	}
}

func (c *MockCars) SetLoadResp(item interface{}) {
	if item != nil {
		c.SelectResponse = item.(*common.Car)
	} else {
		c.SelectResponse = nil
	}
}

func (c *MockCars) GetSoftwareAndPKCVersions(vins []string) (results []common.CarPKCOSVersion, err error) {
	panic("unimplemented")
}
