package cache

import (
	"github.com/fiskerinc/cloud-services/pkg/common"
	"github.com/fiskerinc/cloud-services/pkg/logger"
	"github.com/fiskerinc/cloud-services/pkg/redis"

	redigo "github.com/gomodule/redigo/redis"
	"github.com/pkg/errors"
)


func GetVINListDigitalTwin(vins []string, clientPool redis.ClientPoolInterface) (digitalTwins map[string]common.CarState, errorList []error) {
	digitalTwins = make(map[string]common.CarState)
	client := clientPool.GetFromPool()
	defer client.Close()

	batch := redis.NewRedisBatchCommands()
	for _, vin := range vins {
		batch.Add("SISMEMBER", redis.CarSessionsKey(), vin)
		batch.Add("SISMEMBER", redis.HMISessionsKey(), vin)
		batch.Add("HGETALL", redis.CarStateHashKey(vin))

	}

	payload, err := redigo.Values(client.ExecuteBatch(batch))
	if err != nil {
		errorList = append(errorList, err)
		return
	}
	
	for index, vin := range vins {
		startPoint := index * 3
		tempTwin, err := ParsePayloadForVehicleState(payload[startPoint:startPoint+3])
		if err != nil {
			err = errors.WithMessage(err, vin)
			errorList = append(errorList, err)
			continue
		}
		digitalTwins[vin] = tempTwin
	}

	return
}

func ParsePayloadForVehicleState(payload []interface{}) (common.CarState, error) {
	var state common.CarState

	online, err := redigo.Bool(payload[0], nil)
	if err != nil {
		return state, err
	} else {
		state.Online = online
	}

	online, err = redigo.Bool(payload[1], nil)
	if err != nil {
		return state, errors.WithStack(err)
	} else {
		state.OnlineHMI = online
	}

	err = parseCarStatePayload(&state, payload[2])

	return state, err
}

func parseCarStatePayload(state *common.CarState, payload interface{}) error {
	stateValues, err := redigo.Values(payload, nil)
	if err != nil {
		return err
	}
	if len(stateValues)%2 != 0 {
		return errors.New("object does not contain equal number of key value pairs")
	}

	err = parseStateValues(state, stateValues, parseCarState)

	return err
}

func parseStateValues(state *common.CarState, stateValues []interface{}, parser stateParser) error {
	for i := 0; i < len(stateValues); i += 2 {
		key, okKey := stateValues[i].([]byte)
		value, okValue := stateValues[i+1].([]byte)

		if !okKey || !okValue {
			return errors.New("cannot parse object into car state")
		}

		err := parser(state, string(key), value)
		// log error, do not return error so we can read other properties for digital twin
		if err != nil {
			logger.Err(err).Send()
		}
	}

	return nil
}