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

namespace EventsAppQuest.Web.Application
{
    /// <summary>
    /// Consolida métricas do ciclo completo a partir do dashboard, alertas, closure e dados operacionais. Apenas leitura agregada; heurísticas simples.
    /// </summary>
    public static class EventCycleMetricsController
    {
        private const int MaxPositiveFactors = 5;
        private const int MaxNegativeFactors = 5;
        private const int PenaltyPerScopeRevision = 5;
        private const int PenaltyPerBlockingAlert = 15;

        /// <summary>Calcula métricas para todos os ciclos.</summary>
        public static List<EventCycleMetricsState> CalculateMetrics(
            IReadOnlyList<ProducerOrder> orders,
            IReadOnlyList<SalesOpportunity> opportunities,
            IReadOnlyList<ProposalRecord> proposals,
            IReadOnlyList<SessionRecord> sessions,
            IReadOnlyList<FollowUpTask> followUpTasks)
        {
            var dashboardStates = EventCycleDashboardController.BuildDashboard(orders, opportunities, proposals, sessions);
            var alertStates = EventCycleAlertController.EvaluateAllAlerts(orders, opportunities, proposals, sessions, followUpTasks);
            var closures = EventCycleClosureController.EvaluateClosure(orders, opportunities, proposals, sessions, followUpTasks);
            var list = new List<EventCycleMetricsState>();
            var now = DateTime.UtcNow;
            var opps = opportunities ?? new List<SalesOpportunity>();
            var sess = sessions ?? new List<SessionRecord>();

            foreach (var order in orders ?? new List<ProducerOrder>())
            {
                var orderId = order.OrderId;
                var dashboard = dashboardStates?.FirstOrDefault(s => (s.ProducerOrderId ?? s.CycleId) == orderId);
                var alertState = alertStates?.FirstOrDefault(a => a.CycleId == orderId);
                var closure = closures?.FirstOrDefault(c => c.CycleId == orderId);
                var opportunity = opps.FirstOrDefault(o => o.Id == order.OpportunityId);
                var session = sess.FirstOrDefault(s => s.Id == (order.SessionId ?? opportunity?.SessionRecordId));

                var metrics = CalculateMetricsForCycle(orderId, order, opportunity, session, dashboard, alertState, closure, now);
                if (metrics != null)
                    list.Add(metrics);
            }

            return list;
        }

        /// <summary>Calcula métricas para um ciclo.</summary>
        public static EventCycleMetricsState CalculateMetricsForCycle(
            string cycleId,
            ProducerOrder order,
            SalesOpportunity opportunity,
            SessionRecord session,
            EventCycleDashboardState dashboardState,
            EventCycleAlertState alertState,
            EventCycleClosureState closureState,
            DateTime? calculatedAt = null)
        {
            if (order == null) return null;
            var now = calculatedAt ?? DateTime.UtcNow;

            var revisions = ProducerOrderScopeController.GetRevisionsForOrder(cycleId);
            var revisionCount = revisions?.Count ?? 0;
            var thread = ProducerOrderCommunicationController.GetThreadForOrder(cycleId);
            var readiness = ProducerExecutionReadinessController.Evaluate(cycleId);
            var checklistState = ProducerPreparationChecklistController.GetChecklistState(cycleId);
            var execState = ProducerExecutionTrackingController.GetState(cycleId);
            var finalReport = ProducerExecutionFinalReportController.GetReport(cycleId);
            var finalAcceptance = HotelFinalAcceptanceController.GetState(cycleId);

            var questSessionCount = session != null && !string.IsNullOrEmpty(session.SessionId) ? 1 : 0;
            var questScore = ComputeQuestCompletionSignalScore(session);
            var commercialScore = ComputeCommercialConversionScore(opportunity, session);
            var handoffScore = ComputeHandoffDelayRiskScore(dashboardState);
            var producerScore = ComputeProducerResponseScore(dashboardState, readiness, checklistState);
            var commScore = ComputeCommunicationActivityScore(thread);
            var prepScore = ComputePreparationReadinessScore(checklistState, dashboardState);
            var execScore = ComputeExecutionProgressScore(execState);
            var finalScore = ComputeFinalAcceptanceCompletionScore(finalAcceptance);

            var positives = new List<string>();
            var negatives = new List<string>();

            if (dashboardState != null)
            {
                if (dashboardState.IsFinalAcceptanceDone) positives.Add("Aceite final concluído");
                if (dashboardState.IsExecutionCompleted) positives.Add("Execução concluída");
                if (dashboardState.IsPreparationConfirmed) positives.Add("Preparação confirmada");
                if (dashboardState.IsScopeConfirmed) positives.Add("Escopo confirmado");
                if (dashboardState.IsProducerAccepted) positives.Add("Pedido aceite pelo produtor");
                if (dashboardState.IsQuestSessionDone && questScore >= 70) positives.Add("Sessão Quest com decisão clara");
            }
            if (revisionCount == 0 && (readiness?.IsScopeConfirmed ?? false)) positives.Add("Sem revisões de escopo");
            if (thread != null && thread.LastMessageAt.HasValue) positives.Add("Comunicação operacional ativa");

            if (revisionCount > 0) negatives.Add(revisionCount == 1 ? "1 revisão de escopo" : $"{revisionCount} revisões de escopo");
            if (alertState != null)
            {
                if (alertState.BlockingAlerts > 0) negatives.Add($"{alertState.BlockingAlerts} alerta(s) bloqueante(s)");
                if (alertState.HighSeverityAlerts >= 2) negatives.Add("Múltiplos alertas de alta severidade");
            }
            if (finalReport != null && !string.IsNullOrEmpty(finalReport.BlockingIssuesEncountered)) negatives.Add("Issues/bloqueios no relatório final");
            if (closureState != null && closureState.IsCycleBlocked) negatives.Add("Ciclo bloqueado");
            if (!(dashboardState?.IsProducerAccepted ?? false)) negatives.Add("Pedido sem aceite do produtor");
            if (!(readiness?.IsScopeConfirmed ?? false) && (dashboardState?.IsHandoffGenerated ?? false)) negatives.Add("Escopo não confirmado");

            var overall = 0f;
            var count = 0;
            if (questSessionCount > 0) { overall += questScore; count++; }
            overall += commercialScore; count++;
            overall += handoffScore; count++;
            overall += producerScore; count++;
            overall += commScore; count++;
            overall += prepScore; count++;
            overall += execScore; count++;
            overall += finalScore; count++;
            if (count > 0) overall = overall / count;

            overall -= revisionCount * PenaltyPerScopeRevision;
            overall -= (alertState?.BlockingAlerts ?? 0) * PenaltyPerBlockingAlert;
            overall = Math.Max(0, Math.Min(100, (int)Math.Round(overall)));

            var label = overall >= 80 ? "Excellent" : overall >= 60 ? "Good" : overall >= 40 ? "Fair" : "Poor";

            var estimatedDays = 0;
            if (order.CreatedAt != default && order.LastUpdatedAt.HasValue)
                estimatedDays = Math.Max(0, (int)(order.LastUpdatedAt.Value - order.CreatedAt).TotalDays);
            else if (order.CreatedAt != default)
                estimatedDays = Math.Max(0, (int)(now - order.CreatedAt).TotalDays);

            return new EventCycleMetricsState
            {
                CycleId = cycleId,
                TotalCycleEstimatedDurationDays = estimatedDays,
                QuestSessionCount = questSessionCount,
                QuestCompletionSignalScore = questScore,
                CommercialConversionSignalScore = commercialScore,
                HandoffDelayRiskScore = handoffScore,
                ProducerResponseScore = producerScore,
                ScopeRevisionCount = revisionCount,
                CommunicationActivityScore = commScore,
                PreparationReadinessScore = prepScore,
                ExecutionProgressScore = execScore,
                FinalAcceptanceCompletionScore = finalScore,
                OverallCyclePerformanceScore = (int)overall,
                PerformanceLabel = label,
                TopPositiveFactors = positives.Take(MaxPositiveFactors).ToList(),
                TopNegativeFactors = negatives.Take(MaxNegativeFactors).ToList(),
                LastCalculatedAt = now
            };
        }

        private static int ComputeQuestCompletionSignalScore(SessionRecord session)
        {
            if (session == null) return 0;
            switch (session.FinalDecisionState)
            {
                case WebSessionDecisionState.ReadyToClose: return 100;
                case WebSessionDecisionState.ReadyToAdvance: return 85;
                case WebSessionDecisionState.Interested: return 70;
                case WebSessionDecisionState.NeedsFollowUp: return 50;
                case WebSessionDecisionState.ExplorationOnly: return 25;
                default: return 0;
            }
        }

        private static int ComputeCommercialConversionScore(SalesOpportunity opportunity, SessionRecord session)
        {
            var score = 0;
            if (opportunity != null) score += 30;
            if (!string.IsNullOrEmpty(opportunity?.ProposalRecordId)) score += 25;
            if (session != null && !string.IsNullOrEmpty(session.SessionId)) score += 25;
            if (session != null && (session.FinalDecisionState == WebSessionDecisionState.ReadyToClose || session.FinalDecisionState == WebSessionDecisionState.ReadyToAdvance)) score += 20;
            return Math.Min(100, score);
        }

        private static int ComputeHandoffDelayRiskScore(EventCycleDashboardState dashboard)
        {
            if (dashboard == null) return 0;
            var score = dashboard.IsHandoffGenerated ? 50 : 0;
            if (dashboard.IsProducerAccepted) score += 50;
            return Math.Min(100, score);
        }

        private static int ComputeProducerResponseScore(EventCycleDashboardState dashboard, ProducerExecutionReadinessState readiness, ProducerPreparationChecklistState checklist)
        {
            if (dashboard == null) return 0;
            var score = 0;
            if (dashboard.IsProducerAccepted) score += 30;
            if (dashboard.IsScopeConfirmed) score += 25;
            if (checklist != null && checklist.RequiredItems > 0)
                score += (int)((checklist.CompletedRequiredItems * 25f) / checklist.RequiredItems);
            else if (dashboard.IsPreparationConfirmed) score += 25;
            if (dashboard.IsExecutionCompleted) score += 20;
            return Math.Min(100, score);
        }

        private static int ComputeCommunicationActivityScore(CommunicationThread thread)
        {
            if (thread == null) return 0;
            var score = 50;
            if (thread.LastMessageAt.HasValue)
            {
                var days = (DateTime.UtcNow - thread.LastMessageAt.Value).TotalDays;
                if (days <= 7) score += 50;
                else if (days <= 30) score += 25;
            }
            return Math.Min(100, score);
        }

        private static int ComputePreparationReadinessScore(ProducerPreparationChecklistState checklist, EventCycleDashboardState dashboard)
        {
            if (dashboard != null && dashboard.IsPreparationConfirmed) return 100;
            if (checklist == null || checklist.RequiredItems == 0) return 0;
            return Math.Min(100, (int)((checklist.CompletedRequiredItems * 100f) / checklist.RequiredItems));
        }

        private static int ComputeExecutionProgressScore(ProducerExecutionStatusState execState)
        {
            if (execState == null) return 0;
            if (execState.IsCompleted) return 100;
            return Math.Min(100, Math.Max(0, execState.ProgressPercent));
        }

        private static int ComputeFinalAcceptanceCompletionScore(HotelFinalAcceptanceState finalAcceptance)
        {
            if (finalAcceptance == null) return 0;
            if (finalAcceptance.AcceptanceStatus == HotelFinalAcceptanceStatus.Accepted ||
                finalAcceptance.AcceptanceStatus == HotelFinalAcceptanceStatus.Rejected ||
                finalAcceptance.AcceptanceStatus == HotelFinalAcceptanceStatus.Closed)
                return 100;
            return 50;
        }

        /// <summary>Obtém o maior fator positivo e o maior fator negativo para strip/HUD.</summary>
        public static void GetTopFactor(EventCycleMetricsState metrics, out string topPositive, out string topNegative)
        {
            topPositive = null;
            topNegative = null;
            if (metrics == null) return;
            topPositive = metrics.TopPositiveFactors?.FirstOrDefault();
            topNegative = metrics.TopNegativeFactors?.FirstOrDefault();
        }
    }
}
