package ucca

import (
	"fmt"
	"sort"
)

// ControlStatus represents the implementation status of a control
type ControlStatus string

const (
	ControlImplemented    ControlStatus = "IMPLEMENTED"
	ControlPartial        ControlStatus = "PARTIAL"
	ControlNotImplemented ControlStatus = "NOT_IMPLEMENTED"
	ControlNotApplicable  ControlStatus = "NOT_APPLICABLE"
)

// GapAnalysisRequest is the input for gap analysis
type GapAnalysisRequest struct {
	// Obligations that apply (from assessment)
	Obligations []Obligation `json:"obligations"`
	// Current implementation status of controls
	ControlStatusMap map[string]ControlStatus `json:"control_status_map"` // control_id -> status
}

// GapAnalysisResult is the output of gap analysis
type GapAnalysisResult struct {
	CompliancePercent   float64              `json:"compliance_percent"`   // 0-100
	TotalControls       int                  `json:"total_controls"`
	ImplementedControls int                  `json:"implemented_controls"`
	PartialControls     int                  `json:"partial_controls"`
	MissingControls     int                  `json:"missing_controls"`
	Gaps                []GapItem            `json:"gaps"`
	PriorityActions     []PriorityAction     `json:"priority_actions"`
	ByDomain            map[string]DomainGap `json:"by_domain"`
}

// GapItem represents a single compliance gap
type GapItem struct {
	ControlID       string   `json:"control_id"`
	ControlTitle    string   `json:"control_title"`
	ControlDomain   string   `json:"control_domain"`
	Status          ControlStatus `json:"status"`
	Priority        string   `json:"priority"`
	ObligationIDs   []string `json:"obligation_ids"`
	RequiredByCount int      `json:"required_by_count"`
	Impact          string   `json:"impact"` // "critical", "high", "medium", "low"
}

// PriorityAction is a recommended action to close gaps
type PriorityAction struct {
	Rank        int      `json:"rank"`
	Action      string   `json:"action"`
	ControlIDs  []string `json:"control_ids"`
	Impact      string   `json:"impact"`
	Effort      string   `json:"effort"` // "low", "medium", "high"
}

// DomainGap summarizes gaps per TOM domain
type DomainGap struct {
	DomainID            string  `json:"domain_id"`
	DomainName          string  `json:"domain_name"`
	TotalControls       int     `json:"total_controls"`
	ImplementedControls int     `json:"implemented_controls"`
	CompliancePercent   float64 `json:"compliance_percent"`
}

// TOMGapAnalyzer performs gap analysis between obligations and control implementation
type TOMGapAnalyzer struct {
	mapper   *TOMObligationMapper
	tomIndex *TOMControlIndex
}

// NewTOMGapAnalyzer creates a new gap analyzer
func NewTOMGapAnalyzer(mapper *TOMObligationMapper, tomIndex *TOMControlIndex) *TOMGapAnalyzer {
	return &TOMGapAnalyzer{
		mapper:   mapper,
		tomIndex: tomIndex,
	}
}

// Analyze performs gap analysis
func (g *TOMGapAnalyzer) Analyze(req *GapAnalysisRequest) *GapAnalysisResult {
	result := &GapAnalysisResult{
		Gaps:            []GapItem{},
		PriorityActions: []PriorityAction{},
		ByDomain:        make(map[string]DomainGap),
	}

	// Derive required controls from obligations
	requirements := g.mapper.DeriveControlsFromObligations(req.Obligations)
	result.TotalControls = len(requirements)

	// Track domain stats
	domainTotal := make(map[string]int)
	domainImplemented := make(map[string]int)

	for _, ctrl := range requirements {
		controlID := ctrl.Control.ID
		domain := ctrl.Control.DomainID

		domainTotal[domain]++

		status, hasStatus := req.ControlStatusMap[controlID]
		if !hasStatus {
			status = ControlNotImplemented
		}

		switch status {
		case ControlImplemented:
			result.ImplementedControls++
			domainImplemented[domain]++
		case ControlPartial:
			result.PartialControls++
			// Count partial as 0.5 for domain
			domainImplemented[domain]++ // simplified
			result.Gaps = append(result.Gaps, GapItem{
				ControlID:       controlID,
				ControlTitle:    ctrl.Control.Title,
				ControlDomain:   domain,
				Status:          ControlPartial,
				Priority:        ctrl.Priority,
				ObligationIDs:   ctrl.ObligationIDs,
				RequiredByCount: ctrl.RequiredByCount,
				Impact:          ctrl.Priority,
			})
		case ControlNotImplemented:
			result.MissingControls++
			result.Gaps = append(result.Gaps, GapItem{
				ControlID:       controlID,
				ControlTitle:    ctrl.Control.Title,
				ControlDomain:   domain,
				Status:          ControlNotImplemented,
				Priority:        ctrl.Priority,
				ObligationIDs:   ctrl.ObligationIDs,
				RequiredByCount: ctrl.RequiredByCount,
				Impact:          ctrl.Priority,
			})
		case ControlNotApplicable:
			result.TotalControls-- // Don't count N/A
			domainTotal[domain]--
		}
	}

	// Calculate compliance percent
	if result.TotalControls > 0 {
		implemented := float64(result.ImplementedControls) + float64(result.PartialControls)*0.5
		result.CompliancePercent = (implemented / float64(result.TotalControls)) * 100
	} else {
		result.CompliancePercent = 100
	}

	// Sort gaps by priority
	priorityRank := map[string]int{"critical": 0, "high": 1, "medium": 2, "low": 3}
	sort.Slice(result.Gaps, func(i, j int) bool {
		ri := priorityRank[result.Gaps[i].Priority]
		rj := priorityRank[result.Gaps[j].Priority]
		if ri != rj {
			return ri < rj
		}
		return result.Gaps[i].RequiredByCount > result.Gaps[j].RequiredByCount
	})

	// Build domain gaps
	for domain, total := range domainTotal {
		if total <= 0 {
			continue
		}
		impl := domainImplemented[domain]
		pct := float64(impl) / float64(total) * 100
		domainName := domain
		if ctrls := g.tomIndex.GetControlsByDomain(domain); len(ctrls) > 0 {
			domainName = domain // Use domain ID as name
		}
		result.ByDomain[domain] = DomainGap{
			DomainID:            domain,
			DomainName:          domainName,
			TotalControls:       total,
			ImplementedControls: impl,
			CompliancePercent:   pct,
		}
	}

	// Generate priority actions
	result.PriorityActions = g.generatePriorityActions(result.Gaps)

	return result
}

func (g *TOMGapAnalyzer) generatePriorityActions(gaps []GapItem) []PriorityAction {
	var actions []PriorityAction
	rank := 1

	// Group critical gaps by domain
	domainGaps := make(map[string][]GapItem)
	for _, gap := range gaps {
		if gap.Status == ControlNotImplemented {
			domainGaps[gap.ControlDomain] = append(domainGaps[gap.ControlDomain], gap)
		}
	}

	// Generate actions for domains with most critical gaps
	type domainPriority struct {
		domain   string
		gaps     []GapItem
		critCount int
	}
	var dp []domainPriority
	for domain, gs := range domainGaps {
		crit := 0
		for _, g := range gs {
			if g.Priority == "critical" {
				crit++
			}
		}
		dp = append(dp, domainPriority{domain, gs, crit})
	}
	sort.Slice(dp, func(i, j int) bool {
		if dp[i].critCount != dp[j].critCount {
			return dp[i].critCount > dp[j].critCount
		}
		return len(dp[i].gaps) > len(dp[j].gaps)
	})

	for _, d := range dp {
		if rank > 10 {
			break
		}
		var controlIDs []string
		for _, g := range d.gaps {
			controlIDs = append(controlIDs, g.ControlID)
		}

		impact := "medium"
		if d.critCount > 0 {
			impact = "critical"
		} else if len(d.gaps) > 3 {
			impact = "high"
		}

		effort := "medium"
		if len(d.gaps) > 5 {
			effort = "high"
		} else if len(d.gaps) <= 2 {
			effort = "low"
		}

		actions = append(actions, PriorityAction{
			Rank:       rank,
			Action:     fmt.Sprintf("Domain %s: %d fehlende Controls implementieren", d.domain, len(d.gaps)),
			ControlIDs: controlIDs,
			Impact:     impact,
			Effort:     effort,
		})
		rank++
	}

	return actions
}
