using EonaCat.LogStack.Core;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

// 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.

namespace EonaCat.LogStack.Diagnostics;

/// <summary>
/// Context Snapshot Collector - captures heap snapshots and thread stacks on critical errors.
/// Superior to simple error logging as it provides full context for debugging.
/// </summary>
public class ContextSnapshotCollector
{
    private readonly List<ContextSnapshot> _snapshots = new List<ContextSnapshot>();
    private readonly int _maxSnapshots;
    private readonly object _snapshotsLock = new object();
    private readonly LogLevel _triggerLevel;
    private long _totalSnapshotsCaptured;

    public event EventHandler<ContextSnapshot> SnapshotCaptured;

    public ContextSnapshotCollector(
        LogLevel triggerLevel = LogLevel.Critical,
        int maxSnapshots = 100)
    {
        _triggerLevel = triggerLevel;
        _maxSnapshots = Math.Max(10, maxSnapshots);
    }

    /// <summary>
    /// Captures a context snapshot if the log event meets trigger conditions.
    /// </summary>
    public ContextSnapshot CaptureIfNeeded(LogEvent logEvent)
    {
        if (logEvent.Level < _triggerLevel)
            return null;

        return CaptureSnapshot(logEvent);
    }

    /// <summary>
    /// Captures a full context snapshot for the given log event.
    /// </summary>
    public ContextSnapshot CaptureSnapshot(LogEvent logEvent)
    {

        var snapshot = new ContextSnapshot
        {
            CapturedAt = DateTime.UtcNow,
            LogEvent = logEvent,
            ProcessInfo = CaptureProcessInfo(),
            MemoryInfo = CaptureMemoryInfo(),
            ThreadInfo = CaptureThreadInfo(),
            StackTraces = CaptureStackTraces(),
            EnvironmentVars = CaptureEnvironmentSnapshot()
        };

        lock (_snapshotsLock)
        {
            _snapshots.Add(snapshot);
            if (_snapshots.Count > _maxSnapshots)
                _snapshots.RemoveAt(0);

            _totalSnapshotsCaptured++;
        }

        SnapshotCaptured?.Invoke(this, snapshot);
        return snapshot;
    }

    /// <summary>
    /// Gets the most recent snapshot.
    /// </summary>
    public ContextSnapshot GetLatestSnapshot()
    {
        lock (_snapshotsLock)
        {
            return _snapshots.LastOrDefault();
        }
    }

    /// <summary>
    /// Gets all captured snapshots.
    /// </summary>
    public List<ContextSnapshot> GetSnapshots(int skip = 0, int take = 10)
    {
        lock (_snapshotsLock)
        {
            return _snapshots.Skip(skip).Take(take).ToList();
        }
    }

    /// <summary>
    /// Gets a snapshot by timestamp window.
    /// </summary>
    public ContextSnapshot GetSnapshotNear(DateTime timestamp)
    {
        lock (_snapshotsLock)
        {
            return _snapshots
                .OrderBy(s => Math.Abs((s.CapturedAt - timestamp).TotalSeconds))
                .FirstOrDefault();
        }
    }

    /// <summary>
    /// Gets statistics about captured snapshots.
    /// </summary>
    public SnapshotCollectorStats GetStats()
    {
        lock (_snapshotsLock)
        {
            var levelDistribution = _snapshots
                .GroupBy(s => s.LogEvent.Level)
                .ToDictionary(g => g.Key, g => g.Count());

            return new SnapshotCollectorStats
            {
                TotalCaptured = _totalSnapshotsCaptured,
                CurrentSnapshots = _snapshots.Count,
                MaxCapacity = _maxSnapshots,
                LevelDistribution = levelDistribution,
                OldestSnapshot = _snapshots.FirstOrDefault()?.CapturedAt,
                NewestSnapshot = _snapshots.LastOrDefault()?.CapturedAt
            };
        }
    }

    /// <summary>
    /// Clears all snapshots.
    /// </summary>
    public int ClearSnapshots()
    {
        lock (_snapshotsLock)
        {
            var count = _snapshots.Count;
            _snapshots.Clear();
            return count;
        }
    }

    private ProcessSnapshot CaptureProcessInfo()
    {
        var process = Process.GetCurrentProcess();
        return new ProcessSnapshot
        {
            ProcessId = process.Id,
            ProcessName = process.ProcessName,
            StartTime = process.StartTime,
            TotalProcessorTime = process.TotalProcessorTime,
            UserProcessorTime = process.UserProcessorTime,
            BasePriority = process.BasePriority,
            Handles = 0, // Not available in .NET Standard
            ThreadCount = process.Threads.Count
        };
    }

    private MemorySnapshot CaptureMemoryInfo()
    {
        var totalMemory = GC.GetTotalMemory(false);
        var process = Process.GetCurrentProcess();

        var gen0Collections = GC.CollectionCount(0);
        var gen1Collections = GC.CollectionCount(1);
        var gen2Collections = GC.CollectionCount(2);

        return new MemorySnapshot
        {
            ManagedHeapBytes = totalMemory,
            ProcessWorkingSetBytes = process.WorkingSet64,
            ProcessPrivateMemoryBytes = process.PrivateMemorySize64,
            Gen0Collections = gen0Collections,
            Gen1Collections = gen1Collections,
            Gen2Collections = gen2Collections,
            TotalAllocatedBytes = totalMemory, // Simplified fallback
            IsHighMemoryPressure = false // Conservative estimate
        };
    }

    private ThreadSnapshot CaptureThreadInfo()
    {
        var process = Process.GetCurrentProcess();
        var threads = process.Threads;
        var threadDetails = new List<ThreadDetail>();

        try
        {
            foreach (ProcessThread t in threads)
            {
                threadDetails.Add(new ThreadDetail
                {
                    ThreadId = t.Id,
                    State = t.ThreadState.ToString(),
                    WaitReason = t.WaitReason.ToString(),
                    Priority = t.CurrentPriority
                });
            }
        }
        catch
        {
            // Some properties may not be available on all platforms
        }

        return new ThreadSnapshot
        {
            TotalThreadCount = threads.Count,
            ThreadIds = threadDetails,
            ManagedThreadCount = 0 // ThreadPool.ThreadCount not available in .NET Standard
        };
    }

    private Dictionary<int, string[]> CaptureStackTraces()
    {
        var stacks = new Dictionary<int, string[]>();

        try
        {
            // Get current thread stack
            var currentThread = Thread.CurrentThread;
            var stackFrames = new StackTrace(true).GetFrames();

            if (stackFrames != null)
            {
                stacks[currentThread.ManagedThreadId] = stackFrames
                    .Select(f => f.GetMethod()?.Name ?? "Unknown")
                    .ToArray();
            }
        }
        catch
        {
            // If we can't get stack traces, just skip
        }

        return stacks;
    }

    private Dictionary<string, string> CaptureEnvironmentSnapshot()
    {
        var env = new Dictionary<string, string>();

        // Capture selected environment variables (not all for security)
        var safeVars = new[] 
        { 
            "DOTNET_VERSION", 
            "PROCESSOR_COUNT", 
            "OS", 
            "RUNNING_UNDER_DOCKER",
            "ASPNETCORE_ENVIRONMENT"
        };

        foreach (var varName in safeVars)
        {
            var varValue = Environment.GetEnvironmentVariable(varName);
            if (varValue != null)
                env[varName] = varValue;
        }

        // Add runtime info
        env["RUNTIME_VERSION"] = RuntimeInformation.FrameworkDescription;
        env["PROCESSOR_COUNT"] = Environment.ProcessorCount.ToString();

        return env;
    }
}

/// <summary>
/// A captured context snapshot.
/// </summary>
public class ContextSnapshot
{
    /// <summary>
    /// When this snapshot was captured.
    /// </summary>
    public DateTime CapturedAt { get; set; }

    /// <summary>
    /// The log event that triggered this snapshot.
    /// </summary>
    public LogEvent LogEvent { get; set; }

    /// <summary>
    /// Process information at time of capture.
    /// </summary>
    public ProcessSnapshot ProcessInfo { get; set; }

    /// <summary>
    /// Memory information at time of capture.
    /// </summary>
    public MemorySnapshot MemoryInfo { get; set; }

    /// <summary>
    /// Thread information at time of capture.
    /// </summary>
    public ThreadSnapshot ThreadInfo { get; set; }

    /// <summary>
    /// Stack traces for active threads.
    /// </summary>
    public Dictionary<int, string[]> StackTraces { get; set; }

    /// <summary>
    /// Environment snapshot.
    /// </summary>
    public Dictionary<string, string> EnvironmentVars { get; set; }
}

/// <summary>
/// Process information snapshot.
/// </summary>
public class ProcessSnapshot
{
    public int ProcessId { get; set; }
    public string ProcessName { get; set; }
    public DateTime StartTime { get; set; }
    public TimeSpan TotalProcessorTime { get; set; }
    public TimeSpan UserProcessorTime { get; set; }
    public int BasePriority { get; set; }
    public int Handles { get; set; }
    public int ThreadCount { get; set; }
}

/// <summary>
/// Memory information snapshot.
/// </summary>
public class MemorySnapshot
{
    public long ManagedHeapBytes { get; set; }
    public long ProcessWorkingSetBytes { get; set; }
    public long ProcessPrivateMemoryBytes { get; set; }
    public int Gen0Collections { get; set; }
    public int Gen1Collections { get; set; }
    public int Gen2Collections { get; set; }
    public long TotalAllocatedBytes { get; set; }
    public bool IsHighMemoryPressure { get; set; }
}

/// <summary>
/// Thread information snapshot.
/// </summary>
public class ThreadSnapshot
{
    public int TotalThreadCount { get; set; }
    public List<ThreadDetail> ThreadIds { get; set; }
    public int ManagedThreadCount { get; set; }
}

/// <summary>
/// Detail about a single thread.
/// </summary>
public class ThreadDetail
{
    public int ThreadId { get; set; }
    public string State { get; set; }
    public string WaitReason { get; set; }
    public int Priority { get; set; }
}

/// <summary>
/// Statistics about the snapshot collector.
/// </summary>
public class SnapshotCollectorStats
{
    public long TotalCaptured { get; set; }
    public int CurrentSnapshots { get; set; }
    public int MaxCapacity { get; set; }
    public Dictionary<LogLevel, int> LevelDistribution { get; set; }
    public DateTime? OldestSnapshot { get; set; }
    public DateTime? NewestSnapshot { get; set; }
}

/// <summary>
/// Runtime information helper.
/// </summary>
internal static class RuntimeInformation
{
    public static string FrameworkDescription
    {
        get
        {
#if NET6_0_OR_GREATER
            return System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
#else
            return ".NET " + Environment.Version;
#endif
        }
    }
}
