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.Core;
using EventsAppQuest.Web.Producer.Domain;

namespace EventsAppQuest.Web.Application
{
    /// <summary>
    /// Análise de SLA do ciclo: segmentos temporais, tempo esperado vs observado, risco de atraso. Agregador; usa dashboard, alertas, execução e aceite final.
    /// </summary>
    public static class EventCycleSLAController
    {
        private const int ExpectedLeadToQuestDays = 7;
        private const int ExpectedQuestToHandoffDays = 3;
        private const int ExpectedHandoffToAcceptanceDays = 5;
        private const int ExpectedAcceptanceToPreparationDays = 7;
        private const int ExpectedPreparationToExecutionDays = 3;
        private const int ExpectedExecutionToReportDays = 14;
        private const int ExpectedReportToAcceptanceDays = 5;

        /// <summary>Avalia SLA para todos os ciclos.</summary>
        public static List<EventCycleSLAState> EvaluateSLA(
            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 list = new List<EventCycleSLAState>();
            var now = DateTime.UtcNow;
            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 session = sess.FirstOrDefault(s => s.Id == (order.SessionId ?? dashboard?.SessionId));
                var execState = ProducerExecutionTrackingController.GetState(orderId);
                var finalReport = ProducerExecutionFinalReportController.GetReport(orderId);
                var finalAcceptance = HotelFinalAcceptanceController.GetState(orderId);

                var sla = EvaluateSLAForCycle(order, dashboard, alertState, session, execState, finalReport, finalAcceptance, now);
                if (sla != null)
                    list.Add(sla);
            }

            return list;
        }

        /// <summary>Avalia SLA para um ciclo.</summary>
        public static EventCycleSLAState EvaluateSLAForCycle(
            ProducerOrder order,
            EventCycleDashboardState dashboardState,
            EventCycleAlertState alertState,
            SessionRecord session,
            ProducerExecutionStatusState execState,
            ProducerExecutionFinalReport finalReport,
            HotelFinalAcceptanceState finalAcceptance,
            DateTime? evaluatedAt = null)
        {
            if (order == null) return null;
            var now = evaluatedAt ?? DateTime.UtcNow;
            var cycleId = order.OrderId;

            var elapsedDays = (int)(now - order.CreatedAt).TotalDays;
            if (elapsedDays < 0) elapsedDays = 0;

            var totalExpected = ExpectedLeadToQuestDays + ExpectedQuestToHandoffDays + ExpectedHandoffToAcceptanceDays
                + ExpectedAcceptanceToPreparationDays + ExpectedPreparationToExecutionDays
                + ExpectedExecutionToReportDays + ExpectedReportToAcceptanceDays;

            var sla = new EventCycleSLAState
            {
                CycleId = cycleId,
                TotalEstimatedCycleTimeDays = totalExpected,
                CurrentElapsedCycleTimeDays = elapsedDays,
                LeadToQuestExpectedDays = ExpectedLeadToQuestDays,
                QuestToHandoffExpectedDays = ExpectedQuestToHandoffDays,
                HandoffToProducerAcceptanceExpectedDays = ExpectedHandoffToAcceptanceDays,
                AcceptanceToPreparationExpectedDays = ExpectedAcceptanceToPreparationDays,
                PreparationToExecutionExpectedDays = ExpectedPreparationToExecutionDays,
                ExecutionToFinalReportExpectedDays = ExpectedExecutionToReportDays,
                FinalReportToHotelAcceptanceExpectedDays = ExpectedReportToAcceptanceDays,
                Segments = new List<EventCycleSLASegment>(),
                DelayRiskLevel = EventCycleDelaySeverity.None,
                MainDelayReason = null,
                LastEvaluatedAt = now
            };

            var questTs = session?.LastSessionTimestamp ?? session?.HeldAt;
            var handoffTs = order.GeneratedAt ?? order.CreatedAt;
            var acceptanceTs = order.AcceptedAt;
            var execStartTs = execState?.StartedAt;
            var execEndTs = finalReport?.ClosedByProducerAt ?? (execState?.IsCompleted == true ? execState.LastUpdatedAt : null);
            var hotelAcceptanceTs = finalAcceptance?.AcceptedAt ?? finalAcceptance?.RejectedAt ?? finalAcceptance?.ClosedAt;

            AddSegment(sla, "Lead", "QuestSession", ExpectedLeadToQuestDays, questTs != null ? (int)((DateTime)questTs - order.CreatedAt).TotalDays : -1, dashboardState?.IsQuestSessionDone ?? false, order.CreatedAt, questTs);
            AddSegment(sla, "QuestSession", "Handoff", ExpectedQuestToHandoffDays, questTs != null && handoffTs.HasValue ? (int)((DateTime)handoffTs.Value - (DateTime)questTs).TotalDays : -1, dashboardState?.IsHandoffGenerated ?? false, questTs, handoffTs);
            AddSegment(sla, "Handoff", "ProducerAcceptance", ExpectedHandoffToAcceptanceDays, handoffTs.HasValue && acceptanceTs.HasValue ? (int)((DateTime)acceptanceTs.Value - (DateTime)handoffTs.Value).TotalDays : -1, dashboardState?.IsProducerAccepted ?? false, handoffTs, acceptanceTs);
            AddSegment(sla, "ProducerAcceptance", "Preparation", ExpectedAcceptanceToPreparationDays, -1, dashboardState?.IsPreparationConfirmed ?? false, acceptanceTs, null);
            AddSegment(sla, "Preparation", "ExecutionStarted", ExpectedPreparationToExecutionDays, -1, dashboardState?.IsExecutionStarted ?? false, null, execStartTs);
            AddSegment(sla, "ExecutionStarted", "FinalReport", ExpectedExecutionToReportDays, execStartTs.HasValue && execEndTs.HasValue ? (int)((DateTime)execEndTs.Value - (DateTime)execStartTs.Value).TotalDays : -1, dashboardState?.IsExecutionCompleted ?? false, execStartTs, execEndTs);
            AddSegment(sla, "FinalReport", "HotelAcceptance", ExpectedReportToAcceptanceDays, execEndTs.HasValue && hotelAcceptanceTs.HasValue ? (int)((DateTime)hotelAcceptanceTs.Value - (DateTime)execEndTs.Value).TotalDays : -1, dashboardState?.IsFinalAcceptanceDone ?? false, execEndTs, hotelAcceptanceTs);

            ApplyDelayRules(sla, dashboardState, alertState, execState, finalReport, finalAcceptance);
            sla.DelayRiskLevel = sla.Segments.Any() ? sla.Segments.Max(s => s.DelaySeverity) : EventCycleDelaySeverity.None;
            var worstSegment = sla.Segments.OrderByDescending(s => s.DelaySeverity).FirstOrDefault(s => s.IsDelayed);
            if (worstSegment != null)
                sla.MainDelayReason = $"{worstSegment.FromMilestoneType} → {worstSegment.ToMilestoneType}: {worstSegment.Notes}";

            return sla;
        }

        private static void AddSegment(EventCycleSLAState sla, string from, string to, int expectedDays, int observedDays, bool isCompleted, DateTime? fromTs, DateTime? toTs)
        {
            var isDelayed = false;
            var severity = EventCycleDelaySeverity.None;
            var notes = "";

            if (observedDays >= 0 && expectedDays > 0 && observedDays > expectedDays)
            {
                isDelayed = true;
                var excess = observedDays - expectedDays;
                if (excess >= 14) severity = EventCycleDelaySeverity.Critical;
                else if (excess >= 7) severity = EventCycleDelaySeverity.High;
                else if (excess >= 3) severity = EventCycleDelaySeverity.Medium;
                else severity = EventCycleDelaySeverity.Low;
                notes = $"{observedDays}d (esperado {expectedDays}d)";
            }
            else if (!isCompleted && fromTs.HasValue)
            {
                var refTs = fromTs.Value;
                var waitingDays = (int)(DateTime.UtcNow - refTs).TotalDays;
                if (waitingDays > expectedDays)
                {
                    isDelayed = true;
                    if (waitingDays - expectedDays >= 14) severity = EventCycleDelaySeverity.Critical;
                    else if (waitingDays - expectedDays >= 7) severity = EventCycleDelaySeverity.High;
                    else if (waitingDays - expectedDays >= 3) severity = EventCycleDelaySeverity.Medium;
                    else severity = EventCycleDelaySeverity.Low;
                    notes = $"pendente há {waitingDays}d (esperado {expectedDays}d)";
                }
            }

            var currentObserved = observedDays >= 0 ? observedDays : (fromTs.HasValue && toTs.HasValue ? (int)(toTs.Value - fromTs.Value).TotalDays : -1);
            if (currentObserved < 0 && fromTs.HasValue)
                currentObserved = (int)(DateTime.UtcNow - fromTs.Value).TotalDays;

            sla.Segments.Add(new EventCycleSLASegment
            {
                FromMilestoneType = from,
                ToMilestoneType = to,
                ExpectedDurationDays = expectedDays,
                CurrentOrObservedDurationDays = currentObserved >= 0 ? currentObserved : 0,
                IsDelayed = isDelayed,
                DelaySeverity = severity,
                Notes = notes
            });
        }

        private static void ApplyDelayRules(EventCycleSLAState sla, EventCycleDashboardState dashboard, EventCycleAlertState alertState, ProducerExecutionStatusState execState, ProducerExecutionFinalReport finalReport, HotelFinalAcceptanceState finalAcceptance)
        {
            if (execState != null && execState.ExecutionStatus == ProducerExecutionStatus.Blocked)
            {
                var seg = sla.Segments.FirstOrDefault(s => s.ToMilestoneType == "FinalReport");
                if (seg != null) { seg.IsDelayed = true; seg.DelaySeverity = EventCycleDelaySeverity.Critical; seg.Notes = "Execução bloqueada"; }
            }
            if (dashboard != null && dashboard.IsQuestSessionDone && !dashboard.IsHandoffGenerated)
            {
                var seg = sla.Segments.FirstOrDefault(s => s.FromMilestoneType == "QuestSession" && s.ToMilestoneType == "Handoff");
                if (seg != null && !seg.IsDelayed) { seg.IsDelayed = true; seg.DelaySeverity = EventCycleDelaySeverity.High; seg.Notes = "Handoff pendente após Quest"; }
            }
            if (dashboard != null && dashboard.IsHandoffGenerated && !dashboard.IsProducerAccepted)
            {
                var seg = sla.Segments.FirstOrDefault(s => s.ToMilestoneType == "ProducerAcceptance");
                if (seg != null && !seg.IsDelayed) { seg.IsDelayed = true; seg.DelaySeverity = EventCycleDelaySeverity.Medium; seg.Notes = "Aceite do produtor pendente"; }
            }
            if (finalReport != null && finalReport.ClosedByProducerAt.HasValue && (finalAcceptance == null || finalAcceptance.AcceptanceStatus == HotelFinalAcceptanceStatus.PendingReview))
            {
                var seg = sla.Segments.FirstOrDefault(s => s.ToMilestoneType == "HotelAcceptance");
                if (seg != null && !seg.IsDelayed) { seg.IsDelayed = true; seg.DelaySeverity = EventCycleDelaySeverity.High; seg.Notes = "Aceite final do hotel pendente"; }
            }
        }
    }
}
