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

namespace EonaCat.LogStack
{
    // This file is part of the EonaCat project(s) which is released under the Apache License.
    // See the LICENSE file or go to https://EonaCat.com/License for full license details.

    /// <summary>
    /// LogHeatmapAnalyzer generates ASCII heatmaps to visualize logging frequency patterns over time.
    /// Useful for identifying traffic patterns, spikes, and patterns in log generation.
    /// </summary>
    public sealed class LogHeatmapAnalyzer
    {
        private const char HotCharacter = '█';  // Full block
        private const char WarmCharacter = '▓';  // Dark shade
        private const char CoolCharacter = '▒';  // Medium shade
        private const char ColdCharacter = '░';  // Light shade
        private const char EmptyCharacter = '·';  // Dot for empty

        /// <summary>
        /// Generate a heatmap showing logging frequency over time
        /// </summary>
        /// <param name="events">Log events to analyze</param>
        /// <param name="timeResolutionMinutes">Time bucket size in minutes (e.g., 5 = 5-minute buckets)</param>
        /// <param name="width">Width of the heatmap (default 60)</param>
        /// <param name="height">Height of the heatmap (default 10)</param>
        /// <returns>ASCII art heatmap string</returns>
        public static string GenerateTimelineHeatmap(
            IEnumerable<LogEvent> events,
            int timeResolutionMinutes = 5,
            int width = 60,
            int height = 10)
        {
            if (timeResolutionMinutes <= 0)
                throw new ArgumentException("Time resolution must be positive", nameof(timeResolutionMinutes));

            var eventList = events?.ToList() ?? new List<LogEvent>();
            if (eventList.Count == 0)
                return "No events to visualize";

            var timeBuckets = CreateTimeBuckets(eventList, timeResolutionMinutes);
            if (timeBuckets.Count == 0)
                return "No valid time data";

            var maxCount = timeBuckets.Values.Max();
            if (maxCount == 0)
                return "All buckets empty";

            return RenderHeatmap(timeBuckets, width, height, maxCount);
        }

        /// <summary>
        /// Generate a heatmap by log level distribution
        /// </summary>
        public static string GenerateLevelDistributionHeatmap(IEnumerable<LogEvent> events, int width = 50)
        {
            var eventList = events?.ToList() ?? new List<LogEvent>();
            if (eventList.Count == 0)
                return "No events to visualize";

            var levels = new[] { LogLevel.Trace, LogLevel.Debug, LogLevel.Information, LogLevel.Warning, LogLevel.Error, LogLevel.Critical };
            var levelCounts = levels.ToDictionary(l => l, l => eventList.Count(e => e.Level == l));

            var sb = new StringBuilder();
            sb.AppendLine("Log Level Distribution:");
            sb.AppendLine(new string('=', width));

            var maxCount = levelCounts.Values.Max();
            if (maxCount == 0)
                return "No events";

            foreach (var level in levels)
            {
                var count = levelCounts[level];
                if (count == 0 && level != LogLevel.Information) continue;

                var percentage = (double)count / eventList.Count * 100;
                var barLength = (int)((double)count / maxCount * (width - 20));
                var bar = new string(GetHeatCharacter(percentage), Math.Max(1, barLength));

                sb.AppendLine($"{level,-12} | {bar} {count,5} ({percentage,5:F1}%)");
            }

            return sb.ToString();
        }

        /// <summary>
        /// Generate a heatmap for category distribution
        /// </summary>
        public static string GenerateCategoryHeatmap(IEnumerable<LogEvent> events, int width = 50, int topN = 10)
        {
            var eventList = events?.ToList() ?? new List<LogEvent>();
            if (eventList.Count == 0)
                return "No events to visualize";

            var categoryCounts = eventList
                .GroupBy(e => e.Category ?? "Unknown")
                .OrderByDescending(g => g.Count())
                .Take(topN)
                .ToDictionary(g => g.Key, g => g.Count());

            var sb = new StringBuilder();
            sb.AppendLine($"Top {topN} Categories:");
            sb.AppendLine(new string('=', width));

            var maxCount = categoryCounts.Values.Max();
            if (maxCount == 0)
                return "No categories";

            foreach (var kvp in categoryCounts)
            {
                var category = kvp.Key.Length > 15 ? kvp.Key.Substring(0, 12) + "..." : kvp.Key;
                var count = kvp.Value;
                var percentage = (double)count / eventList.Count * 100;
                var barLength = (int)((double)count / maxCount * (width - 25));
                var bar = new string(GetHeatCharacter(percentage), Math.Max(1, barLength));

                sb.AppendLine($"{category,-15} | {bar} {count,5} ({percentage,5:F1}%)");
            }

            return sb.ToString();
        }

        /// <summary>
        /// Generate a compact inline heatmap for quick visualization
        /// </summary>
        public static string GenerateCompactHeatmap(IEnumerable<LogEvent> events, int buckets = 40)
        {
            var eventList = events?.ToList() ?? new List<LogEvent>();
            if (eventList.Count == 0)
                return string.Empty;

            var timeBuckets = CreateTimeBuckets(eventList, (int)Math.Ceiling((double)GetTimeSpanMinutes(eventList) / buckets));
            var maxCount = timeBuckets.Values.Max();
            if (maxCount == 0)
                return string.Empty;

            var sb = new StringBuilder();
            foreach (var bucket in timeBuckets.OrderBy(kvp => kvp.Key))
            {
                sb.Append(GetHeatCharacter((double)bucket.Value / maxCount));
            }

            return sb.ToString();
        }

        /// <summary>
        /// Generate a 2D heatmap showing activity by hour and day
        /// </summary>
        public static string GenerateActivityMatrixHeatmap(IEnumerable<LogEvent> events)
        {
            var eventList = events?.ToList() ?? new List<LogEvent>();
            if (eventList.Count == 0)
                return "No events";

            // Create a 24x7 matrix (hours x days)
            var hourlyMatrix = new int[24, 7];
            var minDate = new DateTime(eventList.Min(e => e.Timestamp));
            var maxDate = new DateTime(eventList.Max(e => e.Timestamp));
            var daySpan = (int)Math.Ceiling((maxDate - minDate).TotalDays);

            foreach (var evt in eventList)
            {
                var timestamp = new DateTime(evt.Timestamp);
                var hour = timestamp.Hour;
                var dayOfWeek = (int)timestamp.DayOfWeek;
                hourlyMatrix[hour, dayOfWeek]++;
            }

            var sb = new StringBuilder();
            sb.AppendLine("Activity by Hour (rows) and Day of Week (cols):");
            sb.AppendLine("     Sun  Mon  Tue  Wed  Thu  Fri  Sat");
            sb.AppendLine(new string('-', 37));

            var maxValue = 0;
            for (int h = 0; h < 24; h++)
                for (int d = 0; d < 7; d++)
                    maxValue = Math.Max(maxValue, hourlyMatrix[h, d]);

            if (maxValue == 0)
                return "No hourly data";

            for (int h = 0; h < 24; h++)
            {
                sb.Append($"{h:D2}h: ");
                for (int d = 0; d < 7; d++)
                {
                    var value = hourlyMatrix[h, d];
                    var intensity = value == 0 ? 0 : (double)value / maxValue;
                    sb.Append(GetHeatCharacter(intensity));
                    sb.Append("   ");
                }
                sb.AppendLine();
            }

            return sb.ToString();
        }

        // Private helpers

        private static Dictionary<DateTime, int> CreateTimeBuckets(List<LogEvent> events, int bucketMinutes)
        {
            var buckets = new Dictionary<DateTime, int>();

            foreach (var evt in events)
            {
                var timestamp = new DateTime(evt.Timestamp);
                var bucketTime = timestamp.AddSeconds(-timestamp.Second)
                    .AddMilliseconds(-timestamp.Millisecond)
                    .AddMinutes(-(timestamp.Minute % bucketMinutes));

                if (buckets.ContainsKey(bucketTime))
                    buckets[bucketTime]++;
                else
                    buckets[bucketTime] = 1;
            }

            return buckets;
        }

        private static string RenderHeatmap(Dictionary<DateTime, int> timeBuckets, int width, int height, int maxCount)
        {
            var sortedBuckets = timeBuckets.OrderBy(kvp => kvp.Key).ToList();
            var step = Math.Max(1, sortedBuckets.Count / width);

            var sb = new StringBuilder();
            sb.AppendLine($"Timeline Heatmap ({sortedBuckets.Count} time periods):");
            sb.AppendLine(new string('=', width + 4));

            // Render vertical bars
            for (int row = height; row > 0; row--)
            {
                var threshold = (double)maxCount / height * row;
                sb.Append("| ");

                for (int col = 0; col < width; col++)
                {
                    var idx = col * step;
                    if (idx < sortedBuckets.Count)
                    {
                        var count = sortedBuckets[idx].Value;
                        var intensity = (double)count / maxCount;
                        var displayThreshold = (double)(row - 1) / height;

                        sb.Append(intensity > displayThreshold ? HotCharacter : ' ');
                    }
                    else
                    {
                        sb.Append(' ');
                    }
                }

                sb.AppendLine(" |");
            }

            sb.AppendLine(new string('=', width + 4));
            if (sortedBuckets.Count > 0)
                sb.AppendLine($"From: {sortedBuckets.First().Key:yyyy-MM-dd HH:mm}, To: {sortedBuckets.Last().Key:yyyy-MM-dd HH:mm}");

            return sb.ToString();
        }

        private static char GetHeatCharacter(double intensity)
        {
            if (intensity < 0.01) return EmptyCharacter;
            if (intensity < 0.35) return ColdCharacter;
            if (intensity < 0.65) return CoolCharacter;
            if (intensity < 0.85) return WarmCharacter;
            return HotCharacter;
        }

        private static int GetTimeSpanMinutes(List<LogEvent> events)
        {
            if (events.Count == 0) return 1;
            var maxTicks = events.Max(e => e.Timestamp);
            var minTicks = events.Min(e => e.Timestamp);
            var span = new TimeSpan(maxTicks - minTicks);
            return Math.Max(1, (int)span.TotalMinutes);
        }
    }
}
