package ucca

import (
	"fmt"
	"time"
)

// JSONRegulationModule implements RegulationModule from a v2 JSON file
type JSONRegulationModule struct {
	regFile         *V2RegulationFile
	conditionEngine *ObligationConditionEngine
	applicability   func(*UnifiedFacts) bool
}

// NewJSONRegulationModule creates a RegulationModule from a v2 JSON regulation file
func NewJSONRegulationModule(regFile *V2RegulationFile) *JSONRegulationModule {
	m := &JSONRegulationModule{
		regFile:         regFile,
		conditionEngine: NewObligationConditionEngine(),
	}
	// Set regulation-level applicability check
	m.applicability = m.defaultApplicability
	return m
}

func (m *JSONRegulationModule) ID() string          { return m.regFile.Regulation }
func (m *JSONRegulationModule) Name() string         { return m.regFile.Name }
func (m *JSONRegulationModule) Description() string  { return m.regFile.Description }

// IsApplicable checks regulation-level applicability
func (m *JSONRegulationModule) IsApplicable(facts *UnifiedFacts) bool {
	return m.applicability(facts)
}

// defaultApplicability determines if the regulation applies based on regulation ID
func (m *JSONRegulationModule) defaultApplicability(facts *UnifiedFacts) bool {
	switch m.regFile.Regulation {
	case "dsgvo":
		return facts.DataProtection.ProcessesPersonalData &&
			(facts.Organization.EUMember || facts.DataProtection.OffersToEU || facts.DataProtection.MonitorsEUIndividuals)
	case "ai_act":
		return facts.AIUsage.UsesAI
	case "nis2":
		return facts.Sector.PrimarySector != "" && facts.Sector.PrimarySector != "other" &&
			(facts.Organization.MeetsNIS2SizeThreshold() || len(facts.Sector.SpecialServices) > 0)
	case "bdsg":
		return facts.DataProtection.ProcessesPersonalData && facts.Organization.Country == "DE"
	case "ttdsg":
		return (facts.DataProtection.UsesCookies || facts.DataProtection.UsesTracking) &&
			facts.Organization.Country == "DE"
	case "dsa":
		return facts.DataProtection.OperatesPlatform && facts.Organization.EUMember
	case "data_act":
		return facts.Organization.EUMember
	case "eu_machinery":
		return facts.Organization.EUMember && facts.AIUsage.UsesAI
	case "dora":
		return facts.Financial.DORAApplies || facts.Financial.IsRegulated
	case "betrvg":
		return facts.Organization.Country == "DE" && facts.Organization.EmployeeCount >= 5
	case "agg":
		return facts.Organization.Country == "DE"
	default:
		return true
	}
}

// DeriveObligations derives applicable obligations from the JSON data
func (m *JSONRegulationModule) DeriveObligations(facts *UnifiedFacts) []Obligation {
	var result []Obligation

	for _, v2Obl := range m.regFile.Obligations {
		// Check condition
		if v2Obl.AppliesWhenCondition != nil {
			if !m.conditionEngine.Evaluate(v2Obl.AppliesWhenCondition, facts) {
				continue
			}
		} else {
			// Fall back to legacy applies_when string matching
			if !m.evaluateLegacyCondition(v2Obl.AppliesWhen, facts) {
				continue
			}
		}

		result = append(result, m.convertObligation(v2Obl))
	}

	return result
}

// DeriveControls derives applicable controls
func (m *JSONRegulationModule) DeriveControls(facts *UnifiedFacts) []ObligationControl {
	var result []ObligationControl
	for _, ctrl := range m.regFile.Controls {
		result = append(result, ObligationControl{
			ID:              ctrl.ID,
			RegulationID:    m.regFile.Regulation,
			Name:            ctrl.Name,
			Description:     ctrl.Description,
			Category:        ctrl.Category,
			WhatToDo:        ctrl.WhatToDo,
			ISO27001Mapping: ctrl.ISO27001Mapping,
			Priority:        ObligationPriority(mapPriority(ctrl.Priority)),
		})
	}
	return result
}

func (m *JSONRegulationModule) GetDecisionTree() *DecisionTree { return nil }

func (m *JSONRegulationModule) GetIncidentDeadlines(facts *UnifiedFacts) []IncidentDeadline {
	var result []IncidentDeadline
	for _, dl := range m.regFile.IncidentDL {
		var legalBasis []LegalReference
		for _, lb := range dl.LegalBasis {
			legalBasis = append(legalBasis, LegalReference{
				Norm:    lb.Norm,
				Article: lb.Article,
				Title:   lb.Title,
			})
		}
		result = append(result, IncidentDeadline{
			RegulationID: m.regFile.Regulation,
			Phase:        dl.Phase,
			Deadline:     dl.Deadline,
			Content:      dl.Content,
			Recipient:    dl.Recipient,
			LegalBasis:   legalBasis,
		})
	}
	return result
}

func (m *JSONRegulationModule) GetClassification(facts *UnifiedFacts) string {
	switch m.regFile.Regulation {
	case "nis2":
		if facts.Organization.MeetsNIS2LargeThreshold() || facts.Sector.IsKRITIS {
			return string(NIS2EssentialEntity)
		}
		if facts.Organization.MeetsNIS2SizeThreshold() {
			return string(NIS2ImportantEntity)
		}
		if len(facts.Sector.SpecialServices) > 0 {
			return string(NIS2EssentialEntity)
		}
		return string(NIS2NotAffected)
	case "ai_act":
		if facts.AIUsage.HasHighRiskAI {
			return "hochrisiko"
		}
		if facts.AIUsage.HasLimitedRiskAI {
			return "begrenztes_risiko"
		}
		return "minimales_risiko"
	case "dsgvo":
		if facts.DataProtection.IsController {
			if facts.DataProtection.LargeScaleProcessing || facts.DataProtection.ProcessesSpecialCategories {
				return "verantwortlicher_hochrisiko"
			}
			return "verantwortlicher"
		}
		return "auftragsverarbeiter"
	default:
		return "anwendbar"
	}
}

// convertObligation converts a V2Obligation to the framework Obligation struct
func (m *JSONRegulationModule) convertObligation(v2 V2Obligation) Obligation {
	obl := Obligation{
		ID:                v2.ID,
		RegulationID:      m.regFile.Regulation,
		Title:             v2.Title,
		Description:       v2.Description,
		Category:          ObligationCategory(v2.Category),
		Responsible:       ResponsibleRole(v2.Responsible),
		Priority:          ObligationPriority(mapPriority(v2.Priority)),
		AppliesWhen:       v2.AppliesWhen,
		ISO27001Mapping:   v2.ISO27001Mapping,
		HowToImplement:    v2.HowToImplement,
		BreakpilotFeature: v2.BreakpilotFeature,
	}

	// Legal basis
	for _, lb := range v2.LegalBasis {
		obl.LegalBasis = append(obl.LegalBasis, LegalReference{
			Norm:    lb.Norm,
			Article: lb.Article,
			Title:   lb.Title,
		})
	}

	// Sanctions
	if v2.Sanctions != nil {
		obl.Sanctions = &SanctionInfo{
			MaxFine:           v2.Sanctions.MaxFine,
			PersonalLiability: v2.Sanctions.PersonalLiability,
			CriminalLiability: v2.Sanctions.CriminalLiability,
			Description:       v2.Sanctions.Description,
		}
	}

	// Deadline
	if v2.Deadline != nil {
		obl.Deadline = m.convertDeadline(v2.Deadline)
	}

	// Evidence
	for _, ev := range v2.Evidence {
		switch e := ev.(type) {
		case string:
			obl.Evidence = append(obl.Evidence, EvidenceItem{Name: e, Required: true})
		case map[string]interface{}:
			name, _ := e["name"].(string)
			required, _ := e["required"].(bool)
			format, _ := e["format"].(string)
			obl.Evidence = append(obl.Evidence, EvidenceItem{
				Name:     name,
				Required: required,
				Format:   format,
			})
		}
	}

	// Store TOM control IDs in ExternalResources for now
	obl.ExternalResources = v2.TOMControlIDs

	return obl
}

func (m *JSONRegulationModule) convertDeadline(dl *V2Deadline) *Deadline {
	d := &Deadline{
		Type:     DeadlineType(dl.Type),
		Duration: dl.Duration,
		Interval: dl.Interval,
		Event:    dl.Event,
	}
	if dl.Date != "" {
		t, err := time.Parse("2006-01-02", dl.Date)
		if err == nil {
			d.Date = &t
		}
	}
	return d
}

// evaluateLegacyCondition evaluates the legacy applies_when string
func (m *JSONRegulationModule) evaluateLegacyCondition(condition string, facts *UnifiedFacts) bool {
	switch condition {
	case "always":
		return true
	case "controller":
		return facts.DataProtection.IsController
	case "processor":
		return facts.DataProtection.IsProcessor
	case "high_risk":
		return facts.DataProtection.LargeScaleProcessing || facts.DataProtection.SystematicMonitoring || facts.DataProtection.ProcessesSpecialCategories
	case "needs_dpo":
		return facts.DataProtection.RequiresDSBByLaw || facts.Organization.IsPublicAuthority || facts.Organization.EmployeeCount >= 20
	case "uses_processors":
		return facts.DataProtection.UsesExternalProcessor
	case "cross_border":
		return facts.DataProtection.TransfersToThirdCountries || facts.DataProtection.CrossBorderProcessing
	case "uses_ai":
		return facts.AIUsage.UsesAI
	case "high_risk_provider":
		return facts.AIUsage.HasHighRiskAI && facts.AIUsage.IsAIProvider
	case "high_risk_deployer":
		return facts.AIUsage.HasHighRiskAI && facts.AIUsage.IsAIDeployer
	case "high_risk_deployer_fria":
		return facts.AIUsage.HasHighRiskAI && facts.AIUsage.IsAIDeployer && (facts.Organization.IsPublicAuthority || facts.AIUsage.EducationAccess || facts.AIUsage.EmploymentDecisions)
	case "limited_risk":
		return facts.AIUsage.HasLimitedRiskAI || facts.AIUsage.AIInteractsWithNaturalPersons
	case "gpai_provider":
		return facts.AIUsage.UsesGPAI && facts.AIUsage.IsAIProvider
	case "gpai_systemic_risk":
		return facts.AIUsage.GPAIWithSystemicRisk
	default:
		// For NIS2-style conditions with classification
		if condition == "classification != 'nicht_betroffen'" {
			return true // if module is applicable, classification is not "nicht_betroffen"
		}
		if condition == "classification == 'besonders_wichtige_einrichtung'" {
			return facts.Organization.MeetsNIS2LargeThreshold() || facts.Sector.IsKRITIS
		}
		fmt.Printf("Warning: unknown legacy condition: %s\n", condition)
		return true
	}
}

// mapPriority normalizes priority strings to standard enum values
func mapPriority(p string) string {
	switch p {
	case "kritisch", "critical":
		return "critical"
	case "hoch", "high":
		return "high"
	case "mittel", "medium":
		return "medium"
	case "niedrig", "low":
		return "low"
	default:
		return p
	}
}
