package sloppy

import (
	"slices"
	"sync"
	"time"

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

var vinblocker *VINBlocker
var vinblockerOnce sync.Once

// VIN Blocker fetches the list of allowed vins once per day, and stores them locally to check against
func GetVINBlocker() *VINBlocker {
	vinblockerOnce.Do(func() {
		vinblocker = newVINBlocker()
	})
	return vinblocker
}

type VINBlocker struct {
	allowedList  []string // The list of vins to allow
	allowAll     bool     // Should we allow all vins to be used
	sync.RWMutex          // Control overwriting the allowed list
}

func newVINBlocker() (vb *VINBlocker) {
	vb = &VINBlocker{}
	vb.allowAll = true // This will for half a second allow all cars to access cloud
	// create a new thread to get the list of vins everyday
	//On dev, always allow all connections
	if whereami.Environment != whereami.DEVELOPMENT {
		vb.updateVINList() // Run in thread, so no sneaks can happen
		go vb.autoUpdate()
	}
	return vb
}

func (vb *VINBlocker) IsVINAllowed(vin string) (allow bool) {
	// We are allowing all vins to connect
	if vb.allowAll {
		return true
	}

	vb.RLock()
	defer vb.RUnlock()
	// Returns the index of where the string might be, annoying
	_, allow = slices.BinarySearch(vb.allowedList, vin)
	return allow
}

func (vb *VINBlocker) autoUpdate() {
	vb.updateVINList()
	// Going to be pretty actively changed, so should change this to more detect when vin list is diff
	nextRun := time.Now().Add(1 * time.Hour)
	time.AfterFunc(time.Until(nextRun), vb.autoUpdate)
}

func (vb *VINBlocker) updateVINList() {
	tempList := fetchVINList()
	vb.Lock()
	defer vb.Unlock()
	if len(tempList) == 0 {
		vb.allowAll = true
	} else {
		vb.allowAll = false
	}
	vb.allowedList = tempList
}

func fetchVINList() (vinList []string) {
	carsDB := GetCarsDB()
	var err error
	vinList, err = carsDB.GetWhiteListCars()
	if err != nil {
		logger.Err(err).Msg("Failed to vinBlocker list")
	}
	// making sure the results are sorted
	slices.Sort(vinList)
	return
}
