package hwversion

import (
	"fmt"

	"github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/db/queries"
	"github.com/fiskerinc/cloud-services/pkg/logger"
	"github.com/fiskerinc/cloud-services/pkg/utils/whereami"
)

// Because of import loops, I moved this out to its own file and package

func SetHWVersion(um *common.UpdateManifest, vin string, carsDB queries.CarsInterface) (err error) {
	// We want to get the set of the car's ecus and the HW version they are on
	// Then for each ecu we will check if the hardware version is compatible
	// Should provide this sort of action as an API route as well
	carECUMap, err := getCarECUsMap(um, vin, carsDB)
	if err != nil {
		return
	}

	if whereami.Service == whereami.AFTERSALES {
		p, ok := carECUMap["OBC"]
		if ok {
			p.ECU = "PDU"
			delete(carECUMap, "OBC")
			carECUMap["PDU"] = p
		}
	}

	failedECUs := make([]string, 0)
	// We have the list of ecu's and their list of hw version
	// we also have the list of car ecu's and its hw version
	// check that the versions match up
	for _, ume := range um.ECUs {
		// A patch over for trex's that haven't been updated
		if dontVersionCheckECU(ume.ECU) {
			ume.HWVersion = "placeholder"
			ume.HWVersions = []string{}
			continue
		}

		if len(ume.HWVersions) == 0 {
			failedECUs = append(failedECUs, fmt.Sprintf("no hw versions provided for %s", ume.ECU))
			ume.HWVersion = "placeholder"
			ume.HWVersions = []string{}
			continue
		}

		// Get the relevant ecu from the car that is in the manifest
		carECU, ok := carECUMap[ume.ECU]
		if !ok {
			// FAIL
			failedECUs = append(failedECUs, fmt.Sprintf("car missing entry for ecu %s", ume.ECU))
			ume.HWVersion = "placeholder"
			ume.HWVersions = []string{}
			continue
		}

		// Check if the car and the manifest have matching versions
		if doesManifestHaveHardwareVersion(carECU.HWVersion, ume.HWVersions) {
			ume.HWVersion = carECU.HWVersion
			ume.HWVersions = []string{}
		} else {
			// This is a fail, didn't find a match for the hw version
			failedECUs = append(failedECUs, fmt.Sprintf("no matching hw version for %s", carECU.ECU))
			ume.HWVersion = "placeholder"
			ume.HWVersions = []string{}
		}
	}

	if len(failedECUs) != 0 {
		err = fmt.Errorf("manifest %s failed hw versioning for car %s: %v", um.Name, vin, failedECUs)
		logger.Warn().Err(err).Msg("")
	}
	err = nil
	return
}

func getCarECUsMap(um *common.UpdateManifest, vin string, carsDB queries.CarsInterface) (ecuMap map[string]common.CarECU, err error) {
	ecuNameList := um.GetECUs() // Just the ecu name
	filter := common.CarECUFilter{VIN: vin, ECUs: ecuNameList, Unique: true, HWVersionRequired: true}
	carECUs, err := carsDB.GetCarECUs(filter, nil)
	if err != nil {
		return
	}
	ecuMap = carECUsToMap(carECUs)
	return
}

// Using a map just simplifies the check for matching ecu's
func carECUsToMap(ecus []common.CarECU) (ecuMap map[string]common.CarECU) {
	ecuMap = make(map[string]common.CarECU)
	for _, ecu := range ecus {
		ecuMap[ecu.ECU] = ecu
	}
	return
}

// If the ECU is one we don't want to check the version for because the car doesn't have it: TREX and ICC, then we return true and skip it
func dontVersionCheckECU(ecuName string) (dontCheck bool) {
	// These names should match with project-ai/cloud/sp_manifest_generator/Sharepoint/Utils/ValidatorUtils.cs
	switch ecuName {
	case "TREX":
		return true
	case "TBOX":
		return true
	default:
		return false
	}
}

// Check that the manifest has the cars version for the ECU, if it does return it, otherwise return an empty string

func doesManifestHaveHardwareVersion(carECUHWVersion string, manifestECUHWVersions []string) (hasVersion bool) {
	for _, hwVersion := range manifestECUHWVersions {
		if hwVersion == carECUHWVersion {
			// The version matches, so we set the manifests version correctly, and remove this ecu from our list of carECU's
			// Going to skip the removal for now, I don't think it is necessary
			return true
		}
	}
	return false
}
