using System;
using System.Collections.Generic;
using System.Linq;
using EonaCat.LogStack;

namespace EonaCat.LogStack.Features
{
    /// <summary>
    /// Helper utilities for building distributed tracing and correlation dashboards.
    /// Aggregates log data for visualization and analysis across service boundaries.
    /// </summary>
    public class CorrelationDashboardHelper
    {
        private readonly List<CorrelatedActivityTrace> _traces = new();
        private readonly object _lockObject = new();

        /// <summary>
        /// Records a correlated activity for tracing.
        /// </summary>
        public void RecordActivity(string correlationId, string activityName, 
            string serviceName, ActivityStatus status, double durationMs, Exception exception = null)
        {
            if (string.IsNullOrWhiteSpace(correlationId))
                throw new ArgumentNullException(nameof(correlationId));

            lock (_lockObject)
            {
                var trace = _traces.FirstOrDefault(t => 
                    t.CorrelationId.Equals(correlationId, StringComparison.OrdinalIgnoreCase));

                if (trace == null)
                {
                    trace = new CorrelatedActivityTrace 
                    { 
                        CorrelationId = correlationId,
                        StartTime = DateTime.UtcNow,
                        Activities = new List<ActivityRecord>()
                    };
                    _traces.Add(trace);
                }

                trace.Activities.Add(new ActivityRecord
                {
                    Name = activityName,
                    Service = serviceName,
                    Status = status,
                    DurationMs = durationMs,
                    Timestamp = DateTime.UtcNow,
                    Exception = exception?.Message
                });

                trace.EndTime = DateTime.UtcNow;
            }
        }

        /// <summary>
        /// Gets a trace by correlation ID.
        /// </summary>
        public CorrelatedActivityTrace GetTrace(string correlationId)
        {
            if (string.IsNullOrWhiteSpace(correlationId))
                return null;

            lock (_lockObject)
            {
                return _traces.FirstOrDefault(t =>
                    t.CorrelationId.Equals(correlationId, StringComparison.OrdinalIgnoreCase));
            }
        }

        /// <summary>
        /// Gets all traces.
        /// </summary>
        public IEnumerable<CorrelatedActivityTrace> GetAllTraces()
        {
            lock (_lockObject)
            {
                return _traces.ToList();
            }
        }

        /// <summary>
        /// Gets traces by service name.
        /// </summary>
        public IEnumerable<CorrelatedActivityTrace> GetTracesByService(string serviceName)
        {
            if (string.IsNullOrWhiteSpace(serviceName))
                return Enumerable.Empty<CorrelatedActivityTrace>();

            lock (_lockObject)
            {
                return _traces
                    .Where(t => t.Activities.Any(a => a.Service.Equals(serviceName, StringComparison.OrdinalIgnoreCase)))
                    .ToList();
            }
        }

        /// <summary>
        /// Gets traces with performance issues (slow activities).
        /// </summary>
        public IEnumerable<CorrelatedActivityTrace> GetSlowTraces(double thresholdMs = 1000)
        {
            lock (_lockObject)
            {
                return _traces
                    .Where(t => t.TotalDurationMs > thresholdMs)
                    .ToList();
            }
        }

        /// <summary>
        /// Gets traces with errors.
        /// </summary>
        public IEnumerable<CorrelatedActivityTrace> GetFailedTraces()
        {
            lock (_lockObject)
            {
                return _traces
                    .Where(t => t.Activities.Any(a => a.Status == ActivityStatus.Failed))
                    .ToList();
            }
        }

        /// <summary>
        /// Gets dashboard statistics.
        /// </summary>
        public DashboardStatistics GetStatistics()
        {
            lock (_lockObject)
            {
                if (_traces.Count == 0)
                    return new DashboardStatistics();

                var allActivities = _traces.SelectMany(t => t.Activities).ToList();
                var byStatus = allActivities.GroupBy(a => a.Status)
                    .ToDictionary(g => g.Key, g => g.Count());
                var byService = allActivities.GroupBy(a => a.Service)
                    .ToDictionary(g => g.Key, g => g.Count());

                return new DashboardStatistics
                {
                    TotalTraces = _traces.Count,
                    TotalActivities = allActivities.Count,
                    SuccessfulTraces = _traces.Count(t => !t.HasErrors),
                    FailedTraces = _traces.Count(t => t.HasErrors),
                    AverageDurationMs = _traces.Average(t => t.TotalDurationMs),
                    MinDurationMs = _traces.Min(t => t.TotalDurationMs),
                    MaxDurationMs = _traces.Max(t => t.TotalDurationMs),
                    ServiceCount = byService.Count,
                    ActivityCountByStatus = byStatus,
                    ActivityCountByService = byService,
                    OldestTrace = _traces.Min(t => t.StartTime),
                    NewestTrace = _traces.Max(t => t.EndTime)
                };
            }
        }

        /// <summary>
        /// Generates a service dependency map.
        /// </summary>
        public ServiceDependencyMap GetDependencyMap()
        {
            lock (_lockObject)
            {
                var dependencies = new Dictionary<string, HashSet<string>>();

                foreach (var trace in _traces)
                {
                    var services = trace.Activities.Select(a => a.Service).Distinct().ToList();

                    for (int i = 0; i < services.Count - 1; i++)
                    {
                        var from = services[i];
                        var to = services[i + 1];

                        if (!dependencies.ContainsKey(from))
                            dependencies[from] = new HashSet<string>();

                        dependencies[from].Add(to);
                    }
                }

                return new ServiceDependencyMap
                {
                    Dependencies = dependencies.ToDictionary(
                        kvp => kvp.Key,
                        kvp => kvp.Value.ToList())
                };
            }
        }

        /// <summary>
        /// Finds critical paths (slow traces).
        /// </summary>
        public IEnumerable<CriticalPath> FindCriticalPaths(int topCount = 10)
        {
            lock (_lockObject)
            {
                return _traces
                    .OrderByDescending(t => t.TotalDurationMs)
                    .Take(topCount)
                    .Select(t => new CriticalPath
                    {
                        CorrelationId = t.CorrelationId,
                        TotalDurationMs = t.TotalDurationMs,
                        ServiceSequence = t.Activities.Select(a => a.Service).Distinct().ToList(),
                        SlowestActivity = t.Activities.OrderByDescending(a => a.DurationMs).FirstOrDefault(),
                        HasErrors = t.HasErrors
                    })
                    .ToList();
            }
        }

        /// <summary>
        /// Clears all traces.
        /// </summary>
        public void Clear()
        {
            lock (_lockObject)
            {
                _traces.Clear();
            }
        }

        /// <summary>
        /// Removes traces older than specified age.
        /// </summary>
        public int PruneOldTraces(TimeSpan maxAge)
        {
            lock (_lockObject)
            {
                var cutoffTime = DateTime.UtcNow.Subtract(maxAge);
                var removed = _traces.RemoveAll(t => t.EndTime < cutoffTime);
                return removed;
            }
        }

        /// <summary>
        /// Gets trace as Gantt chart data representation.
        /// </summary>
        public GanttChartData GetAsGanttChart(string correlationId)
        {
            var trace = GetTrace(correlationId);
            if (trace == null)
                return null;

            var startTime = trace.Activities.Min(a => a.Timestamp);
            var activities = trace.Activities
                .Select(a => new GanttChartItem
                {
                    Name = $"{a.Service}.{a.Name}",
                    Start = (a.Timestamp - startTime).TotalMilliseconds,
                    Duration = a.DurationMs,
                    Status = a.Status,
                    Color = GetStatusColor(a.Status)
                })
                .ToList();

            return new GanttChartData
            {
                CorrelationId = correlationId,
                Items = activities,
                StartTime = startTime,
                EndTime = trace.EndTime,
                TotalDurationMs = trace.TotalDurationMs
            };
        }

        private string GetStatusColor(ActivityStatus status)
        {
            return status switch
            {
                ActivityStatus.Success => "#4CAF50",
                ActivityStatus.Failed => "#F44336",
                ActivityStatus.Warning => "#FF9800",
                ActivityStatus.Pending => "#2196F3",
                _ => "#9E9E9E"
            };
        }
    }

    /// <summary>
    /// Represents a correlated activity trace for a request/transaction.
    /// </summary>
    public class CorrelatedActivityTrace
    {
        public string CorrelationId { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public List<ActivityRecord> Activities { get; set; } = new();

        public double TotalDurationMs => (EndTime - StartTime).TotalMilliseconds;
        public bool HasErrors => Activities.Any(a => a.Status == ActivityStatus.Failed);
    }

    /// <summary>
    /// Represents a single activity in a trace.
    /// </summary>
    public class ActivityRecord
    {
        public string Name { get; set; }
        public string Service { get; set; }
        public ActivityStatus Status { get; set; }
        public double DurationMs { get; set; }
        public DateTime Timestamp { get; set; }
        public string Exception { get; set; }
    }

    /// <summary>
    /// Activity status enumeration.
    /// </summary>
    public enum ActivityStatus
    {
        Pending = 0,
        Success = 1,
        Failed = 2,
        Warning = 3
    }

    /// <summary>
    /// Dashboard statistics.
    /// </summary>
    public class DashboardStatistics
    {
        public int TotalTraces { get; set; }
        public int TotalActivities { get; set; }
        public int SuccessfulTraces { get; set; }
        public int FailedTraces { get; set; }
        public double AverageDurationMs { get; set; }
        public double MinDurationMs { get; set; }
        public double MaxDurationMs { get; set; }
        public int ServiceCount { get; set; }
        public Dictionary<ActivityStatus, int> ActivityCountByStatus { get; set; } = new();
        public Dictionary<string, int> ActivityCountByService { get; set; } = new();
        public DateTime OldestTrace { get; set; }
        public DateTime NewestTrace { get; set; }
    }

    /// <summary>
    /// Service dependency relationships.
    /// </summary>
    public class ServiceDependencyMap
    {
        public Dictionary<string, List<string>> Dependencies { get; set; } = new();

        public IEnumerable<string> GetAllServices() =>
            Dependencies.Keys.Union(Dependencies.Values.SelectMany(v => v)).Distinct();
    }

    /// <summary>
    /// Represents a critical path in request flow.
    /// </summary>
    public class CriticalPath
    {
        public string CorrelationId { get; set; }
        public double TotalDurationMs { get; set; }
        public List<string> ServiceSequence { get; set; }
        public ActivityRecord SlowestActivity { get; set; }
        public bool HasErrors { get; set; }
    }

    /// <summary>
    /// Gantt chart representation of a trace.
    /// </summary>
    public class GanttChartData
    {
        public string CorrelationId { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public double TotalDurationMs { get; set; }
        public List<GanttChartItem> Items { get; set; } = new();
    }

    /// <summary>
    /// Item in a Gantt chart.
    /// </summary>
    public class GanttChartItem
    {
        public string Name { get; set; }
        public double Start { get; set; }
        public double Duration { get; set; }
        public ActivityStatus Status { get; set; }
        public string Color { get; set; }
    }
}
