package controller

import (
	"fmt"
	"slices"

	"galaxy/game/internal/model/game"

	"github.com/google/uuid"
)

type Cache struct {
	g                              *game.Game
	cacheRaceIndexByID             map[uuid.UUID]int
	cacheFleetIndexByID            map[uuid.UUID]int
	cacheRaceIndexByShipGroupIndex map[int]int
	cacheShipClassByShipGroupIndex map[int]*game.ShipType
	cachePlanetByPlanetNumber      map[uint]*game.Planet
	cacheRelation                  map[int]map[int]game.Relation
}

func NewCache(g *game.Game) *Cache {
	if g == nil {
		panic("NewCache: nil Game passed")
	}
	c := &Cache{
		g: g,
	}
	return c
}

func (c *Cache) StageCommand() {
	c.g.Stage++
}

func (c Cache) Stage() uint {
	return c.g.Stage
}

func (c *Cache) ShipGroupShipClass(groupIndex int) *game.ShipType {
	if len(c.cacheShipClassByShipGroupIndex) == 0 {
		c.cacheShipsAndGroups()
	}
	c.validateShipGroupIndex(groupIndex)
	if v, ok := c.cacheShipClassByShipGroupIndex[groupIndex]; ok {
		return v
	} else {
		panic(fmt.Sprintf("ShipClassByShipGroupIndex: group not found by index=%v", groupIndex))
	}
}

func (c *Cache) RaceIndex(ID uuid.UUID) int {
	if c.cacheRaceIndexByID == nil {
		c.cacheRaceIndexByID = make(map[uuid.UUID]int)
		for i := range c.listRaceIdx() {
			c.cacheRaceIndexByID[c.g.Race[i].ID] = i
		}
	}
	if v, ok := c.cacheRaceIndexByID[ID]; ok {
		return v
	} else {
		panic(fmt.Sprintf("RaceIndex: race not found by ID=%v", ID))
	}
}

func (c *Cache) cacheShipsAndGroups() {
	if c.cacheRaceIndexByShipGroupIndex != nil {
		clear(c.cacheRaceIndexByShipGroupIndex)
	} else {
		c.cacheRaceIndexByShipGroupIndex = make(map[int]int)
	}
	if c.cacheShipClassByShipGroupIndex != nil {
		clear(c.cacheShipClassByShipGroupIndex)
	} else {
		c.cacheShipClassByShipGroupIndex = make(map[int]*game.ShipType)
	}
	for sgi := range c.g.ShipGroups {
		ri := c.RaceIndex(c.g.ShipGroups[sgi].OwnerID)
		c.cacheRaceIndexByShipGroupIndex[sgi] = ri
		sci, ok := ShipClassIndex(c.g, ri, c.g.ShipGroups[sgi].TypeID)
		if !ok {
			panic(fmt.Sprintf("CollectPlanetGroups: ship class not found for race=%q group=%v", c.g.Race[ri].Name, c.g.ShipGroups[sgi].ID))
		}
		c.cacheShipClassByShipGroupIndex[sgi] = &c.g.Race[ri].ShipTypes[sci]
	}
}

func (c *Cache) invalidateShipGroupCache() {
	clear(c.cacheRaceIndexByShipGroupIndex)
	clear(c.cacheShipClassByShipGroupIndex)
}

func (c *Cache) invalidateFleetCache() {
	clear(c.cacheFleetIndexByID)
}

// Helpers

func ShipClassIndex(g *game.Game, ri int, classID uuid.UUID) (int, bool) {
	if len(g.Race) < ri+1 {
		panic(fmt.Sprintf("ShipClass: game race index %d invalid: len=%d", ri, len(g.Race)))
	}
	sti := slices.IndexFunc(g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.ID == classID })
	return sti, sti >= 0
}
