package ucca

import (
	"sort"
)

// TOMObligationMapper provides bidirectional mapping between obligations and TOM controls
type TOMObligationMapper struct {
	tomIndex            *TOMControlIndex
	obligationToControl map[string][]string // obligation_id -> []control_id
	controlToObligation map[string][]string // control_id -> []obligation_id
}

// TOMControlRequirement represents a required TOM control with context
type TOMControlRequirement struct {
	Control          *TOMControl `json:"control"`
	ObligationIDs    []string    `json:"obligation_ids"`
	Priority         string      `json:"priority"`         // highest priority from linked obligations
	RequiredByCount  int         `json:"required_by_count"` // number of obligations requiring this
}

// NewTOMObligationMapper creates a new mapper from TOM index and v2 mapping
func NewTOMObligationMapper(tomIndex *TOMControlIndex, mapping *V2TOMMapping) *TOMObligationMapper {
	m := &TOMObligationMapper{
		tomIndex:            tomIndex,
		obligationToControl: make(map[string][]string),
		controlToObligation: make(map[string][]string),
	}

	if mapping != nil {
		m.obligationToControl = mapping.ObligationToControl
		m.controlToObligation = mapping.ControlToObligation
	}

	return m
}

// NewTOMObligationMapperFromObligations builds the mapper from obligations' tom_control_ids
func NewTOMObligationMapperFromObligations(tomIndex *TOMControlIndex, obligations []V2Obligation) *TOMObligationMapper {
	m := &TOMObligationMapper{
		tomIndex:            tomIndex,
		obligationToControl: make(map[string][]string),
		controlToObligation: make(map[string][]string),
	}

	for _, obl := range obligations {
		for _, controlID := range obl.TOMControlIDs {
			m.obligationToControl[obl.ID] = append(m.obligationToControl[obl.ID], controlID)
			m.controlToObligation[controlID] = append(m.controlToObligation[controlID], obl.ID)
		}
	}

	return m
}

// GetControlsForObligation returns TOM controls linked to an obligation
func (m *TOMObligationMapper) GetControlsForObligation(obligationID string) []*TOMControl {
	controlIDs := m.obligationToControl[obligationID]
	var result []*TOMControl
	for _, id := range controlIDs {
		if ctrl, ok := m.tomIndex.GetControl(id); ok {
			result = append(result, ctrl)
		}
	}
	return result
}

// GetControlIDsForObligation returns control IDs for an obligation
func (m *TOMObligationMapper) GetControlIDsForObligation(obligationID string) []string {
	return m.obligationToControl[obligationID]
}

// GetObligationsForControl returns obligation IDs linked to a control
func (m *TOMObligationMapper) GetObligationsForControl(controlID string) []string {
	return m.controlToObligation[controlID]
}

// DeriveControlsFromObligations takes a list of applicable obligations and returns
// deduplicated, priority-sorted TOM control requirements
func (m *TOMObligationMapper) DeriveControlsFromObligations(obligations []Obligation) []TOMControlRequirement {
	// Collect all required controls with their linking obligations
	controlMap := make(map[string]*TOMControlRequirement)

	priorityRank := map[string]int{"critical": 0, "high": 1, "medium": 2, "low": 3}

	for _, obl := range obligations {
		// Get control IDs from ExternalResources (where tom_control_ids are stored)
		controlIDs := obl.ExternalResources
		if len(controlIDs) == 0 {
			controlIDs = m.obligationToControl[obl.ID]
		}

		for _, controlID := range controlIDs {
			ctrl, ok := m.tomIndex.GetControl(controlID)
			if !ok {
				continue
			}

			if existing, found := controlMap[controlID]; found {
				existing.ObligationIDs = append(existing.ObligationIDs, obl.ID)
				existing.RequiredByCount++
				// Keep highest priority
				if rank, ok := priorityRank[string(obl.Priority)]; ok {
					if existingRank, ok2 := priorityRank[existing.Priority]; ok2 && rank < existingRank {
						existing.Priority = string(obl.Priority)
					}
				}
			} else {
				controlMap[controlID] = &TOMControlRequirement{
					Control:         ctrl,
					ObligationIDs:   []string{obl.ID},
					Priority:        string(obl.Priority),
					RequiredByCount: 1,
				}
			}
		}
	}

	// Convert to slice and sort by priority then required_by_count
	var result []TOMControlRequirement
	for _, req := range controlMap {
		result = append(result, *req)
	}

	sort.Slice(result, func(i, j int) bool {
		ri := priorityRank[result[i].Priority]
		rj := priorityRank[result[j].Priority]
		if ri != rj {
			return ri < rj
		}
		return result[i].RequiredByCount > result[j].RequiredByCount
	})

	return result
}

// AddMapping adds a single obligation->control mapping
func (m *TOMObligationMapper) AddMapping(obligationID, controlID string) {
	m.obligationToControl[obligationID] = appendUnique(m.obligationToControl[obligationID], controlID)
	m.controlToObligation[controlID] = appendUnique(m.controlToObligation[controlID], obligationID)
}

func appendUnique(slice []string, item string) []string {
	for _, s := range slice {
		if s == item {
			return slice
		}
	}
	return append(slice, item)
}
