package ucca

import (
	"fmt"
	"sort"
	"time"

	"github.com/google/uuid"
)

// ============================================================================
// Obligations Registry
// ============================================================================
//
// The registry manages all regulation modules and provides methods to evaluate
// facts against all registered regulations, aggregating the results.
//
// ============================================================================

// ObligationsRegistry manages all regulation modules
type ObligationsRegistry struct {
	modules map[string]RegulationModule
}

// NewObligationsRegistry creates a new registry and registers all default modules.
// It loads v2 JSON modules first; for regulations without v2 JSON, falls back to YAML modules.
func NewObligationsRegistry() *ObligationsRegistry {
	r := &ObligationsRegistry{
		modules: make(map[string]RegulationModule),
	}

	// Try to load v2 JSON modules first
	v2Loaded := r.loadV2Modules()

	// Fall back to YAML modules for regulations not covered by v2
	if !v2Loaded["nis2"] {
		if nis2Module, err := NewNIS2Module(); err == nil {
			r.Register(nis2Module)
		} else {
			fmt.Printf("Warning: Could not load NIS2 module: %v\n", err)
		}
	}

	if !v2Loaded["dsgvo"] {
		if dsgvoModule, err := NewDSGVOModule(); err == nil {
			r.Register(dsgvoModule)
		} else {
			fmt.Printf("Warning: Could not load DSGVO module: %v\n", err)
		}
	}

	if !v2Loaded["ai_act"] {
		if aiActModule, err := NewAIActModule(); err == nil {
			r.Register(aiActModule)
		} else {
			fmt.Printf("Warning: Could not load AI Act module: %v\n", err)
		}
	}

	return r
}

// loadV2Modules attempts to load all v2 JSON regulation modules
func (r *ObligationsRegistry) loadV2Modules() map[string]bool {
	loaded := make(map[string]bool)

	regulations, err := LoadAllV2Regulations()
	if err != nil {
		fmt.Printf("Info: No v2 regulations found, using YAML modules: %v\n", err)
		return loaded
	}

	for regID, regFile := range regulations {
		module := NewJSONRegulationModule(regFile)
		r.Register(module)
		loaded[regID] = true
		fmt.Printf("Loaded v2 regulation module: %s (%d obligations)\n", regID, len(regFile.Obligations))
	}

	return loaded
}

// NewObligationsRegistryWithModules creates a registry with specific modules
func NewObligationsRegistryWithModules(modules ...RegulationModule) *ObligationsRegistry {
	r := &ObligationsRegistry{
		modules: make(map[string]RegulationModule),
	}
	for _, m := range modules {
		r.Register(m)
	}
	return r
}

// Register adds a regulation module to the registry
func (r *ObligationsRegistry) Register(module RegulationModule) {
	r.modules[module.ID()] = module
}

// Unregister removes a regulation module from the registry
func (r *ObligationsRegistry) Unregister(moduleID string) {
	delete(r.modules, moduleID)
}

// GetModule returns a specific module by ID
func (r *ObligationsRegistry) GetModule(moduleID string) (RegulationModule, bool) {
	m, ok := r.modules[moduleID]
	return m, ok
}

// ListModules returns info about all registered modules
func (r *ObligationsRegistry) ListModules() []RegulationInfo {
	var result []RegulationInfo
	for _, m := range r.modules {
		result = append(result, RegulationInfo{
			ID:          m.ID(),
			Name:        m.Name(),
			Description: m.Description(),
		})
	}
	// Sort by ID for consistent output
	sort.Slice(result, func(i, j int) bool {
		return result[i].ID < result[j].ID
	})
	return result
}

// EvaluateAll evaluates all registered modules against the given facts
func (r *ObligationsRegistry) EvaluateAll(tenantID uuid.UUID, facts *UnifiedFacts, orgName string) *ManagementObligationsOverview {
	overview := &ManagementObligationsOverview{
		ID:                    uuid.New(),
		TenantID:              tenantID,
		OrganizationName:      orgName,
		AssessmentDate:        time.Now(),
		CreatedAt:             time.Now(),
		UpdatedAt:             time.Now(),
		ApplicableRegulations: []ApplicableRegulation{},
		Obligations:           []Obligation{},
		RequiredControls:      []ObligationControl{},
		IncidentDeadlines:     []IncidentDeadline{},
	}

	// Track aggregated sanctions
	var maxFine string
	var personalLiability, criminalLiability bool
	var affectedRegulations []string

	// Evaluate each module
	for _, module := range r.modules {
		if module.IsApplicable(facts) {
			// Get classification
			classification := module.GetClassification(facts)

			// Derive obligations
			obligations := module.DeriveObligations(facts)

			// Derive controls
			controls := module.DeriveControls(facts)

			// Get incident deadlines
			incidentDeadlines := module.GetIncidentDeadlines(facts)

			// Add to applicable regulations
			overview.ApplicableRegulations = append(overview.ApplicableRegulations, ApplicableRegulation{
				ID:              module.ID(),
				Name:            module.Name(),
				Classification:  classification,
				Reason:          r.getApplicabilityReason(module, facts, classification),
				ObligationCount: len(obligations),
				ControlCount:    len(controls),
			})

			// Aggregate obligations
			overview.Obligations = append(overview.Obligations, obligations...)

			// Aggregate controls
			overview.RequiredControls = append(overview.RequiredControls, controls...)

			// Aggregate incident deadlines
			overview.IncidentDeadlines = append(overview.IncidentDeadlines, incidentDeadlines...)

			// Track sanctions
			for _, obl := range obligations {
				if obl.Sanctions != nil {
					if obl.Sanctions.MaxFine != "" && (maxFine == "" || len(obl.Sanctions.MaxFine) > len(maxFine)) {
						maxFine = obl.Sanctions.MaxFine
					}
					if obl.Sanctions.PersonalLiability {
						personalLiability = true
					}
					if obl.Sanctions.CriminalLiability {
						criminalLiability = true
					}
					if !containsString(affectedRegulations, module.ID()) {
						affectedRegulations = append(affectedRegulations, module.ID())
					}
				}
			}
		}
	}

	// Sort obligations by priority and deadline
	r.sortObligations(overview)

	// Build sanctions summary
	overview.SanctionsSummary = r.buildSanctionsSummary(maxFine, personalLiability, criminalLiability, affectedRegulations)

	// Generate executive summary
	overview.ExecutiveSummary = r.generateExecutiveSummary(overview)

	return overview
}

// EvaluateSingle evaluates a single module against the given facts
func (r *ObligationsRegistry) EvaluateSingle(moduleID string, facts *UnifiedFacts) (*ManagementObligationsOverview, error) {
	module, ok := r.modules[moduleID]
	if !ok {
		return nil, fmt.Errorf("module not found: %s", moduleID)
	}

	overview := &ManagementObligationsOverview{
		ID:                    uuid.New(),
		AssessmentDate:        time.Now(),
		CreatedAt:             time.Now(),
		UpdatedAt:             time.Now(),
		ApplicableRegulations: []ApplicableRegulation{},
		Obligations:           []Obligation{},
		RequiredControls:      []ObligationControl{},
		IncidentDeadlines:     []IncidentDeadline{},
	}

	if !module.IsApplicable(facts) {
		return overview, nil
	}

	classification := module.GetClassification(facts)
	obligations := module.DeriveObligations(facts)
	controls := module.DeriveControls(facts)
	incidentDeadlines := module.GetIncidentDeadlines(facts)

	overview.ApplicableRegulations = append(overview.ApplicableRegulations, ApplicableRegulation{
		ID:              module.ID(),
		Name:            module.Name(),
		Classification:  classification,
		Reason:          r.getApplicabilityReason(module, facts, classification),
		ObligationCount: len(obligations),
		ControlCount:    len(controls),
	})

	overview.Obligations = obligations
	overview.RequiredControls = controls
	overview.IncidentDeadlines = incidentDeadlines

	r.sortObligations(overview)
	overview.ExecutiveSummary = r.generateExecutiveSummary(overview)

	return overview, nil
}

// GetDecisionTree returns the decision tree for a specific module
func (r *ObligationsRegistry) GetDecisionTree(moduleID string) (*DecisionTree, error) {
	module, ok := r.modules[moduleID]
	if !ok {
		return nil, fmt.Errorf("module not found: %s", moduleID)
	}
	tree := module.GetDecisionTree()
	if tree == nil {
		return nil, fmt.Errorf("module %s does not have a decision tree", moduleID)
	}
	return tree, nil
}

// ============================================================================
// Helper Methods
// ============================================================================

func (r *ObligationsRegistry) getApplicabilityReason(module RegulationModule, facts *UnifiedFacts, classification string) string {
	switch module.ID() {
	case "nis2":
		if classification == string(NIS2EssentialEntity) {
			return "Besonders wichtige Einrichtung aufgrund von Sektor und Größe"
		} else if classification == string(NIS2ImportantEntity) {
			return "Wichtige Einrichtung aufgrund von Sektor und Größe"
		}
		return "NIS2-Richtlinie anwendbar"
	case "dsgvo":
		return "Verarbeitung personenbezogener Daten"
	case "ai_act":
		return "Einsatz von KI-Systemen"
	case "dora":
		return "Reguliertes Finanzunternehmen"
	default:
		return "Regulierung anwendbar"
	}
}

func (r *ObligationsRegistry) sortObligations(overview *ManagementObligationsOverview) {
	// Sort by priority (critical first), then by deadline
	priorityOrder := map[ObligationPriority]int{
		PriorityCritical: 0,
		PriorityHigh:     1,
		PriorityMedium:   2,
		PriorityLow:      3,
	}

	sort.Slice(overview.Obligations, func(i, j int) bool {
		// First by priority
		pi := priorityOrder[overview.Obligations[i].Priority]
		pj := priorityOrder[overview.Obligations[j].Priority]
		if pi != pj {
			return pi < pj
		}

		// Then by deadline (earlier first, nil last)
		di := overview.Obligations[i].Deadline
		dj := overview.Obligations[j].Deadline

		if di == nil && dj == nil {
			return false
		}
		if di == nil {
			return false
		}
		if dj == nil {
			return true
		}

		// For absolute deadlines, compare dates
		if di.Type == DeadlineAbsolute && dj.Type == DeadlineAbsolute {
			if di.Date != nil && dj.Date != nil {
				return di.Date.Before(*dj.Date)
			}
		}

		return false
	})
}

func (r *ObligationsRegistry) buildSanctionsSummary(maxFine string, personal, criminal bool, affected []string) SanctionsSummary {
	var summary string
	if personal && criminal {
		summary = "Hohe Bußgelder möglich. Persönliche Haftung der Geschäftsführung sowie strafrechtliche Konsequenzen bei Verstößen."
	} else if personal {
		summary = "Hohe Bußgelder möglich. Persönliche Haftung der Geschäftsführung bei Verstößen."
	} else if maxFine != "" {
		summary = fmt.Sprintf("Bußgelder bis zu %s bei Verstößen möglich.", maxFine)
	} else {
		summary = "Keine spezifischen Sanktionen identifiziert."
	}

	return SanctionsSummary{
		MaxFinancialRisk:      maxFine,
		PersonalLiabilityRisk: personal,
		CriminalLiabilityRisk: criminal,
		AffectedRegulations:   affected,
		Summary:               summary,
	}
}

func (r *ObligationsRegistry) generateExecutiveSummary(overview *ManagementObligationsOverview) ExecutiveSummary {
	summary := ExecutiveSummary{
		TotalRegulations:    len(overview.ApplicableRegulations),
		TotalObligations:    len(overview.Obligations),
		CriticalObligations: 0,
		UpcomingDeadlines:   0,
		OverdueObligations:  0,
		KeyRisks:            []string{},
		RecommendedActions:  []string{},
		ComplianceScore:     100, // Start at 100, deduct for gaps
	}

	now := time.Now()
	thirtyDaysFromNow := now.AddDate(0, 0, 30)

	for _, obl := range overview.Obligations {
		// Count critical
		if obl.Priority == PriorityCritical {
			summary.CriticalObligations++
			summary.ComplianceScore -= 10
		}

		// Count deadlines
		if obl.Deadline != nil && obl.Deadline.Type == DeadlineAbsolute && obl.Deadline.Date != nil {
			if obl.Deadline.Date.Before(now) {
				summary.OverdueObligations++
				summary.ComplianceScore -= 15
			} else if obl.Deadline.Date.Before(thirtyDaysFromNow) {
				summary.UpcomingDeadlines++
			}
		}
	}

	// Ensure score doesn't go below 0
	if summary.ComplianceScore < 0 {
		summary.ComplianceScore = 0
	}

	// Add key risks
	if summary.CriticalObligations > 0 {
		summary.KeyRisks = append(summary.KeyRisks, fmt.Sprintf("%d kritische Pflichten erfordern sofortige Aufmerksamkeit", summary.CriticalObligations))
	}
	if summary.OverdueObligations > 0 {
		summary.KeyRisks = append(summary.KeyRisks, fmt.Sprintf("%d Pflichten haben überfällige Fristen", summary.OverdueObligations))
	}
	if overview.SanctionsSummary.PersonalLiabilityRisk {
		summary.KeyRisks = append(summary.KeyRisks, "Persönliche Haftungsrisiken für die Geschäftsführung bestehen")
	}

	// Add recommended actions
	if summary.OverdueObligations > 0 {
		summary.RecommendedActions = append(summary.RecommendedActions, "Überfällige Pflichten priorisieren und umgehend adressieren")
	}
	if summary.CriticalObligations > 0 {
		summary.RecommendedActions = append(summary.RecommendedActions, "Kritische Pflichten in der nächsten Vorstandssitzung besprechen")
	}
	if len(overview.IncidentDeadlines) > 0 {
		summary.RecommendedActions = append(summary.RecommendedActions, "Incident-Response-Prozesse gemäß Meldefristen etablieren")
	}

	// Default action if no specific risks
	if len(summary.RecommendedActions) == 0 {
		summary.RecommendedActions = append(summary.RecommendedActions, "Regelmäßige Compliance-Reviews durchführen")
	}

	// Set next review date (3 months from now)
	nextReview := now.AddDate(0, 3, 0)
	summary.NextReviewDate = &nextReview

	return summary
}

