package controller

import (
	"galaxy/model/report"

	"github.com/google/uuid"
)

func TransformBattle(c *Cache, b *Battle) *report.BattleReport {
	r := &report.BattleReport{
		ID:         b.ID,
		Planet:     b.Planet,
		PlanetName: c.MustPlanet(b.Planet).Name,
		Races:      make(map[int]uuid.UUID),
		Ships:      make(map[int]report.BattleReportGroup),
		Protocol:   make([]report.BattleActionReport, len(b.Protocol)),
	}

	cacheShipClass := make(map[uuid.UUID]int)
	cacheRaceName := make(map[uuid.UUID]int)
	processedGroup := make(map[int]bool)

	addShipGroup := func(groupId int, inBattle bool) int {
		shipClass := c.ShipGroupShipClass(groupId)
		sg := c.ShipGroup(groupId)
		// Several ship-groups of the same race/class can take part
		// in the same battle (different tech upgrades, arrivals from
		// different planets, …). They share a single
		// BattleReportGroup entry keyed by ShipClass.ID — when a
		// later group lands on a cached class we add its Number and
		// NumberLeft into the existing entry instead of dropping
		// them, so the protocol's per-class destroy counts reconcile
		// with the recorded totals. `processedGroup` guards against
		// double-counting a single groupId across multiple shots in
		// the protocol — `ship()` runs on every attacker and defender
		// reference, the merge must happen once per groupId.
		if existing, ok := cacheShipClass[shipClass.ID]; ok {
			if !processedGroup[groupId] {
				bg := r.Ships[existing]
				bg.Number += b.InitialNumbers[groupId]
				bg.NumberLeft += sg.Number
				if inBattle {
					bg.InBattle = true
				}
				r.Ships[existing] = bg
				processedGroup[groupId] = true
			}
			return existing
		}
		itemNumber := len(r.Ships)
		bg := &report.BattleReportGroup{
			Race:         c.g.Race[c.RaceIndex(sg.OwnerID)].Name,
			InBattle:     inBattle,
			Number:       b.InitialNumbers[groupId],
			NumberLeft:   sg.Number,
			ClassName:    shipClass.Name,
			LoadType:     sg.CargoString(),
			LoadQuantity: report.F(sg.Load.F()),
			Tech:         make(map[string]report.Float, len(sg.Tech)),
		}
		for t, v := range sg.Tech {
			bg.Tech[t.String()] = report.F(v.F())
		}
		r.Ships[itemNumber] = *bg
		cacheShipClass[shipClass.ID] = itemNumber
		processedGroup[groupId] = true
		return itemNumber
	}

	ship := func(groupId int) int {
		return addShipGroup(groupId, true)
	}

	race := func(groupId int) int {
		race := c.ShipGroupOwnerRace(groupId)
		if v, ok := cacheRaceName[race.ID]; ok {
			return v
		} else {
			itemNumber := len(r.Races)
			r.Races[itemNumber] = race.ID
			cacheRaceName[race.ID] = itemNumber
			return itemNumber
		}
	}

	for i := range b.Protocol {
		r.Protocol[i] = report.BattleActionReport{
			Attacker:          race(b.Protocol[i].Attacker),
			AttackerShipClass: ship(b.Protocol[i].Attacker),
			Defender:          race(b.Protocol[i].Defender),
			DefenderShipClass: ship(b.Protocol[i].Defender),
			Destroyed:         b.Protocol[i].Destroyed,
		}
	}

	for sgi, inBattle := range b.ObserverGroups {
		if !inBattle {
			addShipGroup(sgi, false)
		}
	}

	return r
}
