using System;
using System.Collections.Generic;
using System.Linq;
using EventsAppQuest.Web.Core;
using EventsAppQuest.Web.Domain;
using EventsAppQuest.Web.Producer.Domain;

namespace EventsAppQuest.Web.Application
{
    /// <summary>
    /// Simulação de cenários do ciclo: gera cenários (atual, com ação prioritária, com recuperação, melhor caso, risco sem ação) e compara impacto previsto. Agregador.
    /// </summary>
    public static class EventCycleScenarioSimulationController
    {
        private const int ScoreGainPriorityAction = 10;
        private const int ScoreGainRecovery = 14;
        private const int ScoreGainBestCase = 18;
        private const int ScoreLossNoAction = 5;

        /// <summary>Gera comparação de cenários para todos os ciclos.</summary>
        public static List<EventCycleScenarioComparisonState> RunSimulations(
            IReadOnlyList<ProducerOrder> orders,
            IReadOnlyList<SalesOpportunity> opportunities,
            IReadOnlyList<ProposalRecord> proposals,
            IReadOnlyList<SessionRecord> sessions,
            IReadOnlyList<FollowUpTask> followUpTasks)
        {
            var dashboardStates = EventCycleDashboardController.BuildDashboard(orders, opportunities, proposals, sessions);
            var closures = EventCycleClosureController.EvaluateClosure(orders, opportunities, proposals, sessions, followUpTasks);
            var metricsList = EventCycleMetricsController.CalculateMetrics(orders, opportunities, proposals, sessions, followUpTasks);
            var slaList = EventCycleSLAController.EvaluateSLA(orders, opportunities, proposals, sessions, followUpTasks);
            var bottleneckStates = EventCycleBottleneckController.EvaluateBottlenecks(orders, opportunities, proposals, sessions, followUpTasks);
            var actionPlans = EventCycleActionPlanController.GenerateActionPlans(orders, opportunities, proposals, sessions, followUpTasks);
            var recoveryPlans = EventCycleRecoveryPlanController.GenerateRecoveryPlans(orders, opportunities, proposals, sessions, followUpTasks);

            var list = new List<EventCycleScenarioComparisonState>();
            var now = DateTime.UtcNow;

            foreach (var state in dashboardStates)
            {
                var cycleId = state.ProducerOrderId ?? state.CycleId;
                var closure = closures?.FirstOrDefault(c => c.CycleId == cycleId);
                var metrics = metricsList?.FirstOrDefault(m => m.CycleId == cycleId);
                var sla = slaList?.FirstOrDefault(s => s.CycleId == cycleId);
                var bottleneckState = bottleneckStates?.FirstOrDefault(b => b.CycleId == cycleId);
                var actionPlan = actionPlans?.FirstOrDefault(p => p.CycleId == cycleId);
                var recoveryPlan = recoveryPlans?.FirstOrDefault(r => r.CycleId == cycleId);

                var comparison = RunSimulationForCycle(cycleId, closure, metrics, sla, bottleneckState, actionPlan, recoveryPlan, now);
                if (comparison != null)
                    list.Add(comparison);
            }

            return list;
        }

        /// <summary>Gera comparação de cenários para um ciclo.</summary>
        public static EventCycleScenarioComparisonState RunSimulationForCycle(
            string cycleId,
            EventCycleClosureState closureState,
            EventCycleMetricsState metricsState,
            EventCycleSLAState slaState,
            EventCycleBottleneckState bottleneckState,
            EventCycleActionPlanState actionPlanState,
            EventCycleRecoveryPlanState recoveryPlanState,
            DateTime? simulatedAt = null)
        {
            var now = simulatedAt ?? DateTime.UtcNow;
            var baseClosure = closureState?.ClosureStatus ?? "—";
            var baseScore = metricsState?.OverallCyclePerformanceScore ?? 0;
            var baseDelay = slaState?.DelayRiskLevel ?? EventCycleDelaySeverity.None;

            var scenarios = new List<EventCycleScenarioSimulation>();

            var current = new EventCycleScenarioSimulation
            {
                SimulationId = $"{cycleId}_Scenario_Current",
                CycleId = cycleId,
                ScenarioName = "Estado atual",
                ScenarioType = EventCycleScenarioType.CurrentState,
                BaseClosureStatus = baseClosure,
                SimulatedClosureStatus = baseClosure,
                BasePerformanceScore = baseScore,
                SimulatedPerformanceScore = baseScore,
                BaseDelayRiskLevel = baseDelay,
                SimulatedDelayRiskLevel = baseDelay,
                PredictedImpactSummary = "Estado real atual; sem alteração.",
                GeneratedAt = now
            };
            scenarios.Add(current);

            var topActionId = actionPlanState?.TopPriorityAction?.ActionId ?? recoveryPlanState?.PrimaryRecoveryAction?.RecoveryActionId;
            if (!string.IsNullOrEmpty(topActionId))
            {
                var improvedScore = Math.Min(100, baseScore + ScoreGainPriorityAction);
                var improvedDelay = ImproveDelayLevel(baseDelay, 1);
                var prioritySim = new EventCycleScenarioSimulation
                {
                    SimulationId = $"{cycleId}_Scenario_PriorityAction",
                    CycleId = cycleId,
                    ScenarioName = "Com ação prioritária aplicada",
                    ScenarioType = EventCycleScenarioType.PriorityActionApplied,
                    BaseClosureStatus = baseClosure,
                    SimulatedClosureStatus = closureState?.IsCycleBlocked == true ? "Em andamento" : baseClosure,
                    BasePerformanceScore = baseScore,
                    SimulatedPerformanceScore = improvedScore,
                    BaseDelayRiskLevel = baseDelay,
                    SimulatedDelayRiskLevel = improvedDelay,
                    AssumedCompletedActions = new List<string> { topActionId },
                    PredictedImpactSummary = $"Score +{improvedScore - baseScore}; risco de atraso {(improvedDelay != baseDelay ? baseDelay + "→" + improvedDelay : "mantido")}.",
                    GeneratedAt = now
                };
                scenarios.Add(prioritySim);
            }

            var primaryRecoveryId = recoveryPlanState?.PrimaryRecoveryAction?.RecoveryActionId;
            if (!string.IsNullOrEmpty(primaryRecoveryId) && recoveryPlanState?.IsRecoveryNeeded == true)
            {
                var recScore = Math.Min(100, baseScore + ScoreGainRecovery);
                var recDelay = ImproveDelayLevel(baseDelay, 1);
                var recClosure = closureState?.IsCycleBlocked == true ? "Em andamento" : (closureState?.IsCycleAtRisk == true ? "Saudável" : baseClosure);
                var recoverySim = new EventCycleScenarioSimulation
                {
                    SimulationId = $"{cycleId}_Scenario_Recovery",
                    CycleId = cycleId,
                    ScenarioName = "Com plano de recuperação aplicado",
                    ScenarioType = EventCycleScenarioType.RecoveryPlanApplied,
                    BaseClosureStatus = baseClosure,
                    SimulatedClosureStatus = recClosure,
                    BasePerformanceScore = baseScore,
                    SimulatedPerformanceScore = recScore,
                    BaseDelayRiskLevel = baseDelay,
                    SimulatedDelayRiskLevel = recDelay,
                    AssumedCompletedActions = new List<string> { primaryRecoveryId },
                    AssumedResolvedBottlenecks = recoveryPlanState.PrimaryRecoveryAction?.RelatedBottleneckId != null
                        ? new List<string> { recoveryPlanState.PrimaryRecoveryAction.RelatedBottleneckId }
                        : new List<string>(),
                    PredictedImpactSummary = $"Score +{recScore - baseScore}; closure {baseClosure}→{recClosure}; risco atraso {(recDelay != baseDelay ? baseDelay + "→" + recDelay : "mantido")}.",
                    GeneratedAt = now
                };
                scenarios.Add(recoverySim);
            }

            var blockingCount = bottleneckState?.BlockingBottlenecks?.Count ?? 0;
            if (blockingCount > 0)
            {
                var bestScore = Math.Min(100, baseScore + ScoreGainBestCase);
                var bestDelay = ImproveDelayLevel(baseDelay, 2);
                var assumedBottlenecks = bottleneckState.BlockingBottlenecks.Take(3).Select(b => b.BottleneckId).ToList();
                var bestSim = new EventCycleScenarioSimulation
                {
                    SimulationId = $"{cycleId}_Scenario_BestCase",
                    CycleId = cycleId,
                    ScenarioName = "Melhor caso (curto prazo)",
                    ScenarioType = EventCycleScenarioType.BestCaseShortTerm,
                    BaseClosureStatus = baseClosure,
                    SimulatedClosureStatus = "Saudável",
                    BasePerformanceScore = baseScore,
                    SimulatedPerformanceScore = bestScore,
                    BaseDelayRiskLevel = baseDelay,
                    SimulatedDelayRiskLevel = bestDelay,
                    AssumedResolvedBottlenecks = assumedBottlenecks,
                    PredictedImpactSummary = $"Se principais gargalos forem resolvidos: score +{bestScore - baseScore}; risco {baseDelay}→{bestDelay}.",
                    GeneratedAt = now
                };
                scenarios.Add(bestSim);
            }

            var riskScore = Math.Max(0, baseScore - ScoreLossNoAction);
            var riskDelay = WorsenDelayLevel(baseDelay, 1);
            var riskSim = new EventCycleScenarioSimulation
            {
                SimulationId = $"{cycleId}_Scenario_RiskNoAction",
                CycleId = cycleId,
                ScenarioName = "Risco se nenhuma ação",
                ScenarioType = EventCycleScenarioType.RiskIfNoAction,
                BaseClosureStatus = baseClosure,
                SimulatedClosureStatus = closureState?.IsCycleAtRisk == true ? "Bloqueado" : baseClosure,
                BasePerformanceScore = baseScore,
                SimulatedPerformanceScore = riskScore,
                BaseDelayRiskLevel = baseDelay,
                SimulatedDelayRiskLevel = riskDelay,
                PredictedImpactSummary = baseScore == riskScore && baseDelay == riskDelay
                    ? "Estado mantido se nada for feito."
                    : $"Possível piora: score {(riskScore - baseScore)}; risco {(riskDelay != baseDelay ? baseDelay + "→" + riskDelay : "mantido")}.",
                GeneratedAt = now
            };
            scenarios.Add(riskSim);

            EventCycleScenarioSimulation recommended = null;
            var bestDelta = "";
            var bestScoreDelta = 0;

            foreach (var s in scenarios.Where(x => x.ScenarioType != EventCycleScenarioType.CurrentState && x.ScenarioType != EventCycleScenarioType.RiskIfNoAction))
            {
                var delta = s.SimulatedPerformanceScore - s.BasePerformanceScore;
                if (delta > bestScoreDelta)
                {
                    bestScoreDelta = delta;
                    recommended = s;
                    bestDelta = delta > 0 ? $"+{delta} score" : "";
                    if (s.SimulatedDelayRiskLevel != s.BaseDelayRiskLevel)
                        bestDelta += (bestDelta.Length > 0 ? "; " : "") + $"Risco {s.BaseDelayRiskLevel}→{s.SimulatedDelayRiskLevel}";
                }
            }
            if (recommended == null)
            {
                recommended = current;
                bestDelta = "Sem alteração.";
            }
            else if (string.IsNullOrEmpty(bestDelta))
                bestDelta = $"+{bestScoreDelta} score";

            var summary = $"{scenarios.Count} cenário(s). Recomendado: {recommended.ScenarioName}. {recommended.PredictedImpactSummary}";

            return new EventCycleScenarioComparisonState
            {
                CycleId = cycleId,
                Scenarios = scenarios,
                RecommendedScenario = recommended,
                ComparisonSummary = summary,
                BestImprovementDelta = bestDelta,
                LastSimulatedAt = now
            };
        }

        private static EventCycleDelaySeverity ImproveDelayLevel(EventCycleDelaySeverity current, int steps)
        {
            if (current == EventCycleDelaySeverity.None) return current;
            var order = new[] { EventCycleDelaySeverity.Critical, EventCycleDelaySeverity.High, EventCycleDelaySeverity.Medium, EventCycleDelaySeverity.Low, EventCycleDelaySeverity.None };
            var idx = Array.IndexOf(order, current);
            idx = Math.Max(0, idx - steps);
            return order[idx];
        }

        private static EventCycleDelaySeverity WorsenDelayLevel(EventCycleDelaySeverity current, int steps)
        {
            var order = new[] { EventCycleDelaySeverity.Critical, EventCycleDelaySeverity.High, EventCycleDelaySeverity.Medium, EventCycleDelaySeverity.Low, EventCycleDelaySeverity.None };
            var idx = Array.IndexOf(order, current);
            idx = Math.Min(order.Length - 1, idx + steps);
            return order[idx];
        }
    }
}
