package npcregistry

import (
	"sort"

	"gitea.maximumdirect.net/eric/notarius/internal/core/source"
	"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
	"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/npcs/identity"
	"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
)

type orderedNPCResponse struct {
	value       npcResponse
	earliest    int
	hasEvidence bool
}

func canonicalizeResponse(response *extractionResponse, order shared.SourceRefOrder, sourceID string) {
	if response == nil {
		return
	}
	ordered := make([]orderedNPCResponse, len(response.NPCs))
	for index := range response.NPCs {
		earliest, hasEvidence := canonicalizeNPC(&response.NPCs[index], order, sourceID)
		ordered[index] = orderedNPCResponse{
			value:       response.NPCs[index],
			earliest:    earliest,
			hasEvidence: hasEvidence,
		}
	}
	sort.SliceStable(ordered, func(i, j int) bool {
		if ordered[i].hasEvidence != ordered[j].hasEvidence {
			return ordered[i].hasEvidence
		}
		if !ordered[i].hasEvidence {
			return false
		}
		return ordered[i].earliest < ordered[j].earliest
	})
	for index := range ordered {
		response.NPCs[index] = ordered[index].value
	}
}

func canonicalizeNPC(npc *npcResponse, order shared.SourceRefOrder, sourceID string) (int, bool) {
	if npc == nil {
		return 0, false
	}
	refs := order.Canonicalize(order.OrderEndpoints(canonicalSourceRefs(npc.SourceRefs, sourceID)))
	npc.SourceRefs = npcResponseRefs(refs)
	return order.EarliestValid(refs)
}

func canonicalNPCRegistry(response extractionResponse, sourceID string) dnd.NPCRegistry {
	if response.NPCs == nil {
		return dnd.NPCRegistry{NPCs: nil}
	}
	npcs := make([]dnd.NPC, len(response.NPCs))
	for index, npc := range response.NPCs {
		npcs[index] = dnd.NPC{
			ID:         identity.DeriveID(npc.Name),
			Name:       npc.Name,
			SourceRefs: canonicalSourceRefs(npc.SourceRefs, sourceID),
		}
	}
	return dnd.NPCRegistry{NPCs: npcs}
}

func canonicalSourceRefs(values []npcSourceRefResponse, sourceID string) []source.SourceRef {
	if values == nil {
		return nil
	}
	out := make([]source.SourceRef, len(values))
	for index, value := range values {
		out[index] = source.SourceRef{
			SourceID:    sourceID,
			StartUnitID: value.StartUnitID,
			EndUnitID:   value.EndUnitID,
		}
	}
	return out
}

func npcResponseRefs(values []source.SourceRef) []npcSourceRefResponse {
	if values == nil {
		return nil
	}
	out := make([]npcSourceRefResponse, len(values))
	for index, value := range values {
		out[index] = npcSourceRefResponse{StartUnitID: value.StartUnitID, EndUnitID: value.EndUnitID}
	}
	return out
}
