package cachev2

import (
	"context"

	"github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/logger"
	redis "github.com/fiskerinc/cloud-services/pkg/redisv2"
	"github.com/pkg/errors"

	"github.com/fiskerinc/cloud-services/pkg/db/queries"
)

func GetVINListDigitalTwin(vins []string, redisClient *redis.Connection) (digitalTwins map[string]common.CarState, errorList []error) {
	digitalTwins = make(map[string]common.CarState)

	pipe := redisClient.TxPipeline()
	responses := make([]QueryVehicleStatePreResponse, 0, len(vins))
	for _, vin := range vins {
		rr := QueryVehicleStatePreResponse{}
		rr.CarSessionExists = pipe.SIsMember(context.Background(), redis.CarSessionsKey(), vin)
		rr.HMISessionExists = pipe.SIsMember(context.Background(), redis.HMISessionsKey(), vin)
		rr.CarState = pipe.HGetAll(context.Background(), redis.CarStateHashKey(vin))
		responses = append(responses, rr)
	}

	_, err := pipe.Exec(context.Background())
	if err != nil {
		errorList = append(errorList, err)
		return
	}

	for index, resStruct := range responses {
		bb, err := resStruct.Resolve()
		if err != nil {
			err = errors.Wrapf(err, "VIN: %s", vins[index])
			errorList = append(errorList, err)
			continue
		}
		state, err := ParsePayloadForVehicleState(bb)
		if err != nil {
			err = errors.Wrapf(err, "VIN: %s", vins[index])
			errorList = append(errorList, err)
		}
		if state != nil {
			digitalTwins[vins[index]] = *state
		}
	}

	return
}

type getALDigitalTwinDBFieldsResults struct {
	results []common.CarPKCOSVersion
	err     error
}

func GetVINListALDigitalTwin(vins []string, redisClient *redis.Connection, carsDB queries.CarsInterface) (digitalTwinsAL map[string]common.CarStateAL, errorList []error) {
	digitalTwinsAL = make(map[string]common.CarStateAL)
	dbResultsChan := make(chan getALDigitalTwinDBFieldsResults)
	// While the redis is fetching its stored info, we should go out to the database and fetch the database knowledge we need
	// May want to put this information into a cache
	go getALDigitalTwinDBFields(vins, carsDB, dbResultsChan)

	digitalTwins, errorList := GetVINListDigitalTwin(vins, redisClient)
	dbResults := <-dbResultsChan
	if dbResults.err != nil {
		errorList = append(errorList, dbResults.err)
		return
	}

	for _, dbRes := range dbResults.results {
		temp := common.CarStateAL{}
		dt, ok := digitalTwins[dbRes.Vin]
		if !ok {
			logger.Warn().Str("VIN", dbRes.Vin).Msg("AL Digital Twin Missing Redis")
			// Think I need to initiate it so we don't null memory maybe?
			dt = common.CarState{}
		}
		temp.CarState = &dt
		temp.OSVersion = dbRes.OSVersion
		temp.PKCVersion = dbRes.PKCVersion
		temp.SumsVersion = dbRes.SumsVersion
		digitalTwinsAL[dbRes.Vin] = temp
	}

	return
}

// IDK how I feel about having this database functionality inside /cache
func getALDigitalTwinDBFields(vins []string, carsDB queries.CarsInterface, out chan getALDigitalTwinDBFieldsResults) {
	results, err := carsDB.GetSoftwareAndPKCVersions(vins)
	out <- getALDigitalTwinDBFieldsResults{
		results: results,
		err:     err,
	}
}

type ALDB interface {
	GetCars() queries.CarsInterface
}
