package policy

import (
	"context"
	"fmt"
	"os"

	"gopkg.in/yaml.v3"
)

// Loader handles loading policy configuration from YAML files.
type Loader struct {
	store *Store
}

// NewLoader creates a new Loader instance.
func NewLoader(store *Store) *Loader {
	return &Loader{store: store}
}

// LoadFromFile loads policy configuration from a YAML file.
func (l *Loader) LoadFromFile(ctx context.Context, path string) error {
	data, err := os.ReadFile(path)
	if err != nil {
		return fmt.Errorf("failed to read YAML file: %w", err)
	}

	config, err := ParseYAML(data)
	if err != nil {
		return fmt.Errorf("failed to parse YAML: %w", err)
	}

	return l.store.LoadFromYAML(ctx, config)
}

// ParseYAML parses YAML configuration data.
func ParseYAML(data []byte) (*BundeslaenderConfig, error) {
	// First, parse as a generic map to handle the inline Bundeslaender
	var rawConfig map[string]interface{}
	if err := yaml.Unmarshal(data, &rawConfig); err != nil {
		return nil, fmt.Errorf("failed to parse YAML: %w", err)
	}

	config := &BundeslaenderConfig{
		Bundeslaender: make(map[string]PolicyConfig),
	}

	// Parse federal
	if federal, ok := rawConfig["federal"]; ok {
		if federalMap, ok := federal.(map[string]interface{}); ok {
			config.Federal = parsePolicyConfig(federalMap)
		}
	}

	// Parse default_operations
	if ops, ok := rawConfig["default_operations"]; ok {
		if opsMap, ok := ops.(map[string]interface{}); ok {
			config.DefaultOperations = parseOperationsConfig(opsMap)
		}
	}

	// Parse pii_rules
	if rules, ok := rawConfig["pii_rules"]; ok {
		if rulesSlice, ok := rules.([]interface{}); ok {
			for _, rule := range rulesSlice {
				if ruleMap, ok := rule.(map[string]interface{}); ok {
					config.PIIRules = append(config.PIIRules, parsePIIRuleConfig(ruleMap))
				}
			}
		}
	}

	// Parse Bundeslaender (2-letter codes)
	bundeslaender := []string{"BW", "BY", "BE", "BB", "HB", "HH", "HE", "MV", "NI", "NW", "RP", "SL", "SN", "ST", "SH", "TH"}
	for _, bl := range bundeslaender {
		if blConfig, ok := rawConfig[bl]; ok {
			if blMap, ok := blConfig.(map[string]interface{}); ok {
				config.Bundeslaender[bl] = parsePolicyConfig(blMap)
			}
		}
	}

	return config, nil
}

func parsePolicyConfig(m map[string]interface{}) PolicyConfig {
	pc := PolicyConfig{}

	if name, ok := m["name"].(string); ok {
		pc.Name = name
	}

	if sources, ok := m["sources"].([]interface{}); ok {
		for _, src := range sources {
			if srcMap, ok := src.(map[string]interface{}); ok {
				pc.Sources = append(pc.Sources, parseSourceConfig(srcMap))
			}
		}
	}

	return pc
}

func parseSourceConfig(m map[string]interface{}) SourceConfig {
	sc := SourceConfig{
		TrustBoost: 0.5, // Default
	}

	if domain, ok := m["domain"].(string); ok {
		sc.Domain = domain
	}
	if name, ok := m["name"].(string); ok {
		sc.Name = name
	}
	if license, ok := m["license"].(string); ok {
		sc.License = license
	}
	if legalBasis, ok := m["legal_basis"].(string); ok {
		sc.LegalBasis = legalBasis
	}
	if citation, ok := m["citation_template"].(string); ok {
		sc.CitationTemplate = citation
	}
	if trustBoost, ok := m["trust_boost"].(float64); ok {
		sc.TrustBoost = trustBoost
	}

	return sc
}

func parseOperationsConfig(m map[string]interface{}) OperationsConfig {
	oc := OperationsConfig{}

	if lookup, ok := m["lookup"].(map[string]interface{}); ok {
		oc.Lookup = parseOperationConfig(lookup)
	}
	if rag, ok := m["rag"].(map[string]interface{}); ok {
		oc.RAG = parseOperationConfig(rag)
	}
	if training, ok := m["training"].(map[string]interface{}); ok {
		oc.Training = parseOperationConfig(training)
	}
	if export, ok := m["export"].(map[string]interface{}); ok {
		oc.Export = parseOperationConfig(export)
	}

	return oc
}

func parseOperationConfig(m map[string]interface{}) OperationConfig {
	oc := OperationConfig{}

	if allowed, ok := m["allowed"].(bool); ok {
		oc.Allowed = allowed
	}
	if requiresCitation, ok := m["requires_citation"].(bool); ok {
		oc.RequiresCitation = requiresCitation
	}

	return oc
}

func parsePIIRuleConfig(m map[string]interface{}) PIIRuleConfig {
	rc := PIIRuleConfig{
		Severity: "block", // Default
	}

	if name, ok := m["name"].(string); ok {
		rc.Name = name
	}
	if ruleType, ok := m["type"].(string); ok {
		rc.Type = ruleType
	}
	if pattern, ok := m["pattern"].(string); ok {
		rc.Pattern = pattern
	}
	if severity, ok := m["severity"].(string); ok {
		rc.Severity = severity
	}

	return rc
}

// LoadDefaults loads a minimal set of default data (for testing or when no YAML exists).
func (l *Loader) LoadDefaults(ctx context.Context) error {
	// Create federal policy with KMK
	federalPolicy, err := l.store.CreatePolicy(ctx, &CreateSourcePolicyRequest{
		Name: "KMK & Bundesebene",
	})
	if err != nil {
		return fmt.Errorf("failed to create federal policy: %w", err)
	}

	trustBoost := 0.95
	legalBasis := "Amtliche Werke (§5 UrhG)"
	citation := "Quelle: KMK, {title}, {date}"

	_, err = l.store.CreateSource(ctx, &CreateAllowedSourceRequest{
		PolicyID:         federalPolicy.ID,
		Domain:           "kmk.org",
		Name:             "Kultusministerkonferenz",
		License:          LicenseParagraph5,
		LegalBasis:       &legalBasis,
		CitationTemplate: &citation,
		TrustBoost:       &trustBoost,
	})
	if err != nil {
		return fmt.Errorf("failed to create KMK source: %w", err)
	}

	// Create default PII rules
	defaultRules := DefaultPIIRules()
	for _, rule := range defaultRules {
		_, err := l.store.CreatePIIRule(ctx, &CreatePIIRuleRequest{
			Name:     rule.Name,
			RuleType: PIIRuleType(rule.Type),
			Pattern:  rule.Pattern,
			Severity: PIISeverity(rule.Severity),
		})
		if err != nil {
			return fmt.Errorf("failed to create PII rule %s: %w", rule.Name, err)
		}
	}

	return nil
}

// HasData checks if the policy tables already have data.
func (l *Loader) HasData(ctx context.Context) (bool, error) {
	policies, _, err := l.store.ListPolicies(ctx, &PolicyListFilter{Limit: 1})
	if err != nil {
		return false, err
	}
	return len(policies) > 0, nil
}

// LoadIfEmpty loads data from YAML only if tables are empty.
func (l *Loader) LoadIfEmpty(ctx context.Context, path string) error {
	hasData, err := l.HasData(ctx)
	if err != nil {
		return err
	}

	if hasData {
		return nil // Already has data, skip loading
	}

	// Check if file exists
	if _, err := os.Stat(path); os.IsNotExist(err) {
		// File doesn't exist, load defaults
		return l.LoadDefaults(ctx)
	}

	return l.LoadFromFile(ctx, path)
}
