package carcommand

import (
	"context"

	"github.com/fiskerinc/cloud-services/pkg/cache"
	"github.com/fiskerinc/cloud-services/pkg/db/queries"
	"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
	"github.com/fiskerinc/cloud-services/pkg/logger"
	"github.com/fiskerinc/cloud-services/pkg/redis"
	"github.com/fiskerinc/cloud-services/pkg/utils/envtool"
	"github.com/fiskerinc/cloud-services/pkg/validator"

	"github.com/pkg/errors"
)

type CarWakeUp struct {
	db  queries.CarsInterface
	sms sms.SMSServiceClient
}

func NewCarWakeUp(db queries.CarsInterface, sms sms.SMSServiceClient) *CarWakeUp {
	return &CarWakeUp{
		db:  db,
		sms: sms,
	}
}

func (c *CarWakeUp) WakeUp(vin string, await bool) error {
	logger.Debug().Msgf("CarWakeUp.WakeUp %s", vin)
	car, err := c.db.SelectByVIN(vin)
	if err != nil {
		return err
	}

	if car.ICCID == "" {
		return errors.WithStack(ErrNoICCIDForWakeUp)
	}

	// KafkaServiceTarget is where the sms delivery status will be sent if await is false
	// Need to be added to aftersales and ota_update
	smsWakeUp := sms.SendSMSRequest{
		ICCID:              car.ICCID,
		MessageText:        ".",
		Await:              await,
		KafkaServiceTarget: envtool.GetEnv("SMS_SERVICE_KAFKA_CALLBACK", "attendant_service"),
	}

	if ok, err := validator.ValidateICCIDSimple(smsWakeUp.ICCID); !ok || err != nil {
		err = errors.New("iccid " + smsWakeUp.ICCID + " is invalid")
		logger.Warn().Err(errors.WithStack(err)).Send()
		return err
	}
	_, err = c.sms.HandleSMSSend(context.Background(), &smsWakeUp)
	if err != nil {
		logger.Error().Err(errors.WithStack(err)).Send()
		return errors.WithStack(ErrWakeUpMessageNotSent)
	}

	return nil
}

func (c *CarWakeUp) WakeUpQueue(vin string, await bool) (qs *sms.SMSQueueResponse, err error) {
	logger.Debug().Msgf("CarWakeUp.WakeUpQueue %s", vin)
	car, err := c.db.SelectByVIN(vin)
	if err != nil {
		return qs, err
	}

	// If we do not return an ICCID, we still send the message to the SMS service.
	// The sms service will create a fake wake up message to be used on dev

	smsWakeUp := sms.SendSMSRequest{
		ICCID:       car.ICCID,
		MessageText: ".",
		Await:       await,
	}

	qs, err = c.sms.HandleSMSQueue(context.Background(), &smsWakeUp)
	if err != nil {
		logger.Error().Err(errors.WithStack(err)).Send()
		return qs, errors.WithStack(ErrWakeUpMessageNotSent)
	}

	return
}

// The SMS service currently automatically always calls back to the attendant service, which is not great, as config updates are not sent that way
func QueueSMSWakeUp(vin string, await bool, kafkaclient redis.Client, carDB queries.CarsInterface, sms sms.SMSServiceClient) (qs *sms.SMSQueueResponse, err error) {
	// This new client with every request is bad. These are re-usable
	wake := NewCarWakeUp(carDB, sms)
	qs, err = wake.WakeUpQueue(vin, await)
	if err != nil {
		level := logger.Error()
		if errors.Is(err, ErrNoICCIDForWakeUp) ||
			errors.Is(err, ErrWakeUpMessageNotSent) {
			level = logger.Warn()
		}
		logger.At(level, vin, "sms").Err(err).Send()
	}
	return
}

func SMSWakeUp(vin string, checkonline bool, clientPool redis.ClientPoolInterface, carDB queries.CarsInterface, sms sms.SMSServiceClient) (err error) {
	if checkonline {
		var online bool
		online, err = cache.IsCarOnline(clientPool, vin)
		if err != nil {
			logger.At(logger.Error(), vin, "sms").Err(err).Send()
			return
		}
		if online {
			return nil
		}
	}

	wake := NewCarWakeUp(carDB, sms)
	err = wake.WakeUp(vin, true)
	if err != nil {
		level := logger.Error()
		if errors.Is(err, ErrNoICCIDForWakeUp) ||
			errors.Is(err, ErrWakeUpMessageNotSent) {
			level = logger.Warn()
		}
		logger.At(level, vin, "sms").Err(err).Send()
	}
	return
}
