package iace

import (
	"encoding/json"
	"fmt"
)

// ============================================================================
// Completeness Types
// ============================================================================

// GateDefinition describes a single completeness gate with its check function.
type GateDefinition struct {
	ID          string
	Category    string // onboarding, classification, hazard_risk, evidence, tech_file
	Label       string
	Required    bool
	Recommended bool
	CheckFunc   func(ctx *CompletenessContext) bool
}

// CompletenessContext provides all project data needed to evaluate completeness gates.
type CompletenessContext struct {
	Project                  *Project
	Components               []Component
	Classifications          []RegulatoryClassification
	Hazards                  []Hazard
	Assessments              []RiskAssessment
	Mitigations              []Mitigation
	Evidence                 []Evidence
	TechFileSections         []TechFileSection
	HasAI                    bool
	PatternMatchingPerformed bool // set from audit trail (entity_type="pattern_matching")
}

// CompletenessResult contains the aggregated result of all gate checks.
type CompletenessResult struct {
	Score             float64            `json:"score"`
	Gates             []CompletenessGate `json:"gates"`
	PassedRequired    int                `json:"passed_required"`
	TotalRequired     int                `json:"total_required"`
	PassedRecommended int                `json:"passed_recommended"`
	TotalRecommended  int                `json:"total_recommended"`
	CanExport         bool               `json:"can_export"`
}

// ============================================================================
// CompletenessChecker
// ============================================================================

// CompletenessChecker evaluates the 25 CE completeness gates for an IACE project.
type CompletenessChecker struct{}

// NewCompletenessChecker creates a new CompletenessChecker instance.
func NewCompletenessChecker() *CompletenessChecker { return &CompletenessChecker{} }

// Check evaluates all 25 completeness gates against the provided context and
// returns an aggregated result with a weighted score.
//
// Scoring formula:
//
//	score = (passed_required / total_required) * 80
//	      + (passed_recommended / total_recommended) * 15
//	      + (passed_optional / total_optional) * 5
//
// Optional gates are those that are neither required nor recommended.
// CanExport is true only when all required gates have passed.
func (c *CompletenessChecker) Check(ctx *CompletenessContext) CompletenessResult {
	gates := buildGateDefinitions()

	var result CompletenessResult
	var passedOptional, totalOptional int

	for _, gate := range gates {
		passed := gate.CheckFunc(ctx)

		details := ""
		if !passed {
			details = fmt.Sprintf("Gate %s not satisfied: %s", gate.ID, gate.Label)
		}

		result.Gates = append(result.Gates, CompletenessGate{
			ID:       gate.ID,
			Category: gate.Category,
			Label:    gate.Label,
			Required: gate.Required,
			Passed:   passed,
			Details:  details,
		})

		switch {
		case gate.Required:
			result.TotalRequired++
			if passed {
				result.PassedRequired++
			}
		case gate.Recommended:
			result.TotalRecommended++
			if passed {
				result.PassedRecommended++
			}
		default:
			// Optional gate (neither required nor recommended)
			totalOptional++
			if passed {
				passedOptional++
			}
		}
	}

	// Calculate weighted score
	result.Score = calculateWeightedScore(
		result.PassedRequired, result.TotalRequired,
		result.PassedRecommended, result.TotalRecommended,
		passedOptional, totalOptional,
	)

	// CanExport is true only when ALL required gates pass
	result.CanExport = result.PassedRequired == result.TotalRequired

	return result
}

// ============================================================================
// Helper Functions
// ============================================================================

// hasMetadataKey checks whether a JSON metadata blob contains a non-empty value
// for the given key.
func hasMetadataKey(metadata json.RawMessage, key string) bool {
	if metadata == nil {
		return false
	}

	var m map[string]interface{}
	if err := json.Unmarshal(metadata, &m); err != nil {
		return false
	}

	val, exists := m[key]
	if !exists {
		return false
	}

	// Check that the value is not empty/nil
	switch v := val.(type) {
	case string:
		return v != ""
	case nil:
		return false
	default:
		return true
	}
}

// hasClassificationFor checks whether a classification exists for the given regulation type.
func hasClassificationFor(classifications []RegulatoryClassification, regulation RegulationType) bool {
	for _, c := range classifications {
		if c.Regulation == regulation {
			return true
		}
	}
	return false
}

// hasTechFileSection checks whether a tech file section of the given type exists.
func hasTechFileSection(sections []TechFileSection, sectionType string) bool {
	for _, s := range sections {
		if s.SectionType == sectionType {
			return true
		}
	}
	return false
}

// calculateWeightedScore computes the weighted completeness score (0-100).
//
// Formula:
//
//	score = (passedRequired/totalRequired) * 80
//	      + (passedRecommended/totalRecommended) * 15
//	      + (passedOptional/totalOptional) * 5
//
// If any denominator is 0, that component contributes 0 to the score.
func calculateWeightedScore(passedRequired, totalRequired, passedRecommended, totalRecommended, passedOptional, totalOptional int) float64 {
	var score float64

	if totalRequired > 0 {
		score += (float64(passedRequired) / float64(totalRequired)) * 80
	}
	if totalRecommended > 0 {
		score += (float64(passedRecommended) / float64(totalRecommended)) * 15
	}
	if totalOptional > 0 {
		score += (float64(passedOptional) / float64(totalOptional)) * 5
	}

	return score
}
