package main

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/google/uuid"
	"samhofi.us/x/keybase/v2/types/chat1"
)

func reset(m chat1.MsgSummary) {
	_, err := k.KVDelete(&m.Channel.Name, "teslabot", "authtok")
	if err != nil {
		handleError(err,m,"There was an error resetting your authentication. Contact @rudi9719 for more information with code %+v")
		return
	}
	_, err = k.KVDelete(&m.Channel.Name, "teslabot", "startPass")
	if err != nil {
		handleError(err,m,"There was an error resetting your authentication. Contact @rudi9719 for more information with code %+v")
		return
	}
}

func authenticate(m chat1.MsgSummary) {
	defer log.PanicSafe()
	if isAuthenticated(m) {
		k.SendMessageByConvID(m.ConvID, "You have already authenticated (please use !reset to reset.")
		return
	}
	if !m.IsEphemeral {
		k.SendMessageByConvID(m.ConvID, "Please remember to delete your message after we have authenticated!")
	}
	parts := strings.Split(m.Content.Text.Body, " ")
	if len(parts) != 3 {
		k.SendMessageByConvID(m.ConvID, "Invalid input for command authenticate. Requires username and password. This information is not stored in keybase, or logged.")
		return
	}
	username := parts[1]
	password := parts[2]
	t, err := login(context.Background(), username, password)
	if err != nil {
		handleError(err,m,"There was an error logging in. Contact @rudi9719 for more information with code %+v")
		return
	}
	log.LogDebug("Token created for %+v", m.Sender.Username)
	_, err = k.KVPut(&m.Channel.Name, "teslabot", "authtok", t)
	if err != nil {
		handleError(err,m,"There was an error storing your auth token. Contact @rudi9719 for more information with code %+v")
		return
	}
	k.ReactByConvID(m.ConvID, m.Id, ":car:")
	k.DeleteByConvID(m.ConvID, m.Id)
	k.SendMessageByConvID(m.ConvID, "You're all set!")
}

func listVehicles(m chat1.MsgSummary) {
	c := getTeslaClient(m)
	if c == nil {
		return
	}
	v, err := c.Vehicles()
	if err != nil {
		handleError(err,m,"There was an error listing vehicles. Contact @rudi9719 for more information with code %+v")
		return
	}
	ret := "Detected vehicles for account: ```"
	for _, v := range v {
		ret += fmt.Sprintf("VIN: %s\n", v.Vin)
		ret += fmt.Sprintf("Name: %s\n\n", v.DisplayName)
	}
	ret += "```"
	k.SendMessageByConvID(m.ConvID, ret)
}

func deferTime(m chat1.MsgSummary) {
	parts := strings.Split(m.Content.Text.Body, " ")
	t := parts[1]
	start := time.Now()
	command := strings.Join(parts[2:], " ")
	m.Content.Text.Body = fmt.Sprintf("!%+v", command)
	timer, err := time.ParseDuration(t)
	if err != nil {
		handleError(err,m,"There was an error parsing your time input. Contact @rudi9719 for more information with code %+v")
		return
	}
	k.SendMessageByConvID(m.ConvID, fmt.Sprintf("I will run `%+v` at %+v", command, time.Now().Add(timer).Format("Jan _2 15:04:05")))
	time.Sleep(timer)
	k.SendMessageByConvID(m.ConvID, fmt.Sprintf("Running `%+v` from %+v", command, start.Format("Jan _2 15:04:05")))
	handleChat(m)
}

func honk(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	err := v.HonkHorn()
	if err != nil {
		handleError(err, m,"There was an error honking your horn. Contact @rudi9719 for more information with code %+v")
		return
	}
	k.SendMessageByConvID(m.ConvID, "I've honked your horn!")
}

func chargeStatus(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	state, err := v.ChargeState()
	if err != nil {
		handleError(err,m,"There was an error getting charge state. Contact @rudi9719 for more information with code %+v")
		return
	}
	ret := fmt.Sprintf("Status for %+v: ```", v.DisplayName)
	ret += fmt.Sprintf("\nCurrent Charge: %+v (%+vmi)", state.BatteryLevel, state.BatteryRange)
	if state.ChargingState != "Disconnected" {
		ret += fmt.Sprintf("\nConnected Cable: %+v", state.ConnChargeCable)
		ret += fmt.Sprintf("\nCharging State: %+v", state.ChargingState)
		if state.ChargingState != "Stopped" {
			ret += fmt.Sprintf("\nTime to full: %+vmin", state.MinutesToFullCharge)
			if state.FastChargerPresent {
				ret += fmt.Sprintf("\nFast Charger: %+v %+v", state.FastChargerBrand, state.FastChargerType)
			}
		}
	}
	ret += "```\n"
	if state.BatteryHeaterOn {
		ret += "The battery heater is on. "
	}
	if state.ChargePortDoorOpen && state.ChargingState == "Disconnected" {
		ret += "The charge port is open. "
	}
	k.SendMessageByConvID(m.ConvID, ret)
}

func flashLights(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	err := v.FlashLights()
	if err != nil {
		handleError(err,m,"There was an error flashing your lights. Contact @rudi9719 for more information with code %+v")
		return
	}

	k.SendMessageByConvID(m.ConvID, "I've flashed your lights!")
}

func currentTemp(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	guiSettings, err := v.GuiSettings()
	if err != nil {
		handleError(err,m,"There was an error getting your preferences. Contact @rudi9719 for more information with code %+v")
		return
	}
	climateState, err := v.ClimateState()
	if err != nil {
		handleError(err, m, "There was an error getting your Climate State. Contact @rudi9719 for more information with code %+v")
		return
	}
	tempSetting := climateState.DriverTempSetting
	insideTemp := climateState.InsideTemp
	if guiSettings.GuiTemperatureUnits == "F" {
		tempSetting = (climateState.DriverTempSetting * 1.8) + 32
		insideTemp = (climateState.InsideTemp * 1.8) + 32
	}
	if climateState.IsClimateOn {
		k.SendMessageByConvID(m.ConvID, "Your climate on and set to %.0f, current temp is: %.0f inside %+v",
			tempSetting, insideTemp, v.DisplayName)
	} else {
		k.SendMessageByConvID(m.ConvID, "Your climate off but set to %.0f, current temp is %.0f inside %+v",
			tempSetting, insideTemp, v.DisplayName)
	}
}

func setClimate(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	guiSettings, err := v.GuiSettings()
	if err != nil {
		handleError(err, m, "There was an error getting your preferences. Contact @rudi9719 for more information with code %+v")
		return
	}
	parts := strings.Split(m.Content.Text.Body, " ")
	for _, val := range parts {
		if val == "on" {
			err := v.StartAirConditioning()
			if err != nil {
				handleError(err, m, "There was an error starting your Climate. Contact @rudi9719 for more information with code %+v")
				return
			}
		}
		if val == "off" {
			err := v.StopAirConditioning()
			if err != nil {
				handleError(err, m, "There was an error turning off your Climate. Contact @rudi9719 for more information with code %+v")
				return
			}
		}
		if temp, err := strconv.Atoi(val); err == nil {
			if guiSettings.GuiTemperatureUnits == "F" {
				temp = (temp - 32) * 5 / 9
			}
			err = v.SetTemperature(float64(temp), float64(temp))
			if err != nil {
				handleError(err, m, "There was an error setting your Climate. Contact @rudi9719 for more information with code %+v")
				return
			}
			err = v.StartAirConditioning()
			if err != nil {
				handleError(err, m, "There was an error starting your Climate. Contact @rudi9719 for more information with code %+v")
				return
			}
		}
	}
	climateState, err := v.ClimateState()
	if err != nil {
		handleError(err, m, "There was an error getting your Climate State. Contact @rudi9719 for more information with code %+v")
		return
	}
	tempSetting := climateState.DriverTempSetting
	insideTemp := climateState.InsideTemp
	if guiSettings.GuiTemperatureUnits == "F" {
		tempSetting = (climateState.DriverTempSetting * 1.8) + 32
		insideTemp = (climateState.InsideTemp * 1.8) + 32
	}
	if climateState.IsClimateOn {
		k.SendMessageByConvID(m.ConvID, "Your climate on and set to %.0f, current temp is: %.0f", tempSetting, insideTemp)
	} else {
		k.SendMessageByConvID(m.ConvID, "Your climate off but set to %.0f", tempSetting)
	}

}

func lockVehicle(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	err := v.LockDoors()
	if err != nil {
		handleError(err, m, "There was an error locking your doors. Contact @rudi9719 for more information with code %+v")
		return
	}
}

func unlockVehicle(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	err := v.UnlockDoors()
	if err != nil {
		handleError(err, m, "There was an error unlocking your doors. Contact @rudi9719 for more information with code %+v")
		return
	}
}

func startCharge(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	state, err := v.ChargeState()
	if err != nil {
		handleError(err, m, "There was an error getting charge state. Contact @rudi9719 for more information with code %+v")
		return
	}
	if state.ChargingState != "Disconnected" && state.FastChargerBrand != "Tesla" {
		err := v.StartCharging()
		if err != nil {
			handleError(err, m, "There was an error starting your charge. Contact @rudi9719 for more information with code %+v")
			return
		}
	} else {
		k.SendMessageByConvID(m.ConvID, "You must plug in to an L1/L2 charger to use this command.")
	}

}

func stopCharge(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	state, err := v.ChargeState()
	if err != nil {
		handleError(err, m, "There was an error getting charge state. Contact @rudi9719 for more information with code %+v")
		return
	}
	if state.ChargingState != "Disconnected" && state.FastChargerBrand != "Tesla" {
		err := v.StopCharging()
		if err != nil {
			handleError(err, m, "There was an error stopping your charge. Contact @rudi9719 for more information with code %+v")
			return
		}
	} else {
		k.SendMessageByConvID(m.ConvID, "You must plug in to an L1/L2 charger to use this command.")
	}
}

func enableStart(m chat1.MsgSummary) {
	parts := strings.Split(m.Content.Text.Body, " ")
	if len(parts) != 3 {
		k.SendMessageByConvID(m.ConvID, "You must 'accept' this command and supply your password. This command is not as safe, as it stores your password in KVStore.")
		return
	}
	if !strings.Contains(parts[1], "accept") {
		k.SendMessageByConvID(m.ConvID, "You must 'accept' this command and supply your password. This command is not as safe, as it stores your password in KVStore.")
		return
	}
	_, err := k.KVPut(&m.Channel.Name, "teslabot", "startPass", parts[2])
	if err != nil {
		handleError(err, m, "There was an error storing your password. Contact @rudi9719 for more information with code %+v")
		return
	}

}

func disableStart(m chat1.MsgSummary) {
	_, err := k.KVDelete(&m.Channel.Name, "teslabot", "startPass")
	if err != nil {
		handleError(err, m, "There was an error deleting your password. Contact @rudi9719 for more information with code %+v")
		return
	}
}

func startVehicle(m chat1.MsgSummary) {
	v := getVehicle(m)
	if v == nil {
		return
	}
	test, _ := k.KVGet(&m.Channel.Name, "teslabot", "startPass")
	if test.EntryValue == "" {
		k.SendMessageByConvID(m.ConvID, "You must first !enablestart to use this command.")
		return
	}
	err := v.Start(test.EntryValue)
	if err != nil {
		handleError(err, m, "There was an error starting your vehicle. Contact @rudi9719 for more information with code %+v")
		return
	}
	k.SendMessageByConvID(m.ConvID, "Your vehicle has been started!")

}

func openTrunk(m chat1.MsgSummary) {
	v := getVehicle(m)

	parts := strings.Split(m.Content.Text.Body, " ")

	if len(parts) != 2 {
		k.SendMessageByConvID(m.ConvID, "You must supply front or rear.")
		return
	}

	trunk := parts[1]

	switch trunk {
	case "rear":
	case "front":
		err := v.OpenTrunk(strings.ToLower(trunk))
		if err != nil {
			handleError(err, m, "There was an error opening your trunk. Contact @rudi9719 for more information with code %+v")
			return
		}
	default:
		k.SendMessageByConvID(m.ConvID, "You must supply front or rear.")
		return
	}

}
func handleError(err error, m chat1.MsgSummary, msg string) {
	tracker := uuid.NewString()
	log.LogError("%+v: %+v", tracker, err)
	if strings.HasPrefix(err.Error(), "405") {
		k.SendMessageByConvID(m.ConvID, "Tesla returned an error. Please ensure your vehicle isn't currently being serviced.")
		return
	}
	if strings.HasPrefix(err.Error(), "408") {
		k.SendMessageByConvID(m.ConvID, "Unable to wake vehicle within the timeframe. Trying to run the command again.")
		handleChat(m)
		return
	}
	if strings.HasPrefix(err.Error(), "400") {
		k.SendMessageByConvID(m.ConvID, "Tesla returned an error. The command you tried to use may need an update. Please contact @rudi9719 with tracking ID %+v and the bad command.", tracker)
		return
	}
	k.SendMessageByConvID(m.ConvID, msg, tracker)
}