using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using static EonaCat.Logger.Managers.LogHelper;

// 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.Logger.Managers;

/// <summary>
/// Provides a collection of predefined and customizable header token resolvers for formatting log or message headers.
/// </summary>
/// <remarks>HeaderTokens exposes a set of standard tokens that can be used to insert runtime, environment, and
/// contextual information into header strings, such as timestamps, process details, thread information, and more.
/// Custom tokens can be added or overridden to support application-specific formatting needs. All token keys are
/// case-insensitive. This class is thread-safe for read operations, but adding or overriding tokens is not guaranteed
/// to be thread-safe and should be synchronized if used concurrently.</remarks>
public class HeaderTokens
{
    private readonly Dictionary<string, Func<HeaderContext, string>> _tokenResolvers =
           new(StringComparer.OrdinalIgnoreCase)
           {
        ["date"] = ctx => $"[Date: {ctx.Timestamp:yyyy-MM-dd}]",

        ["time"] = ctx => $"[Time: {ctx.Timestamp:HH:mm:ss.fff}]",

        ["ticks"] = ctx => $"[Ticks: {ctx.Timestamp.Ticks}]",

        ["ts"] = ctx => $"[{ctx.Timestamp.ToString(ctx.TimestampFormat)}]",

        ["tz"] = ctx => $"[{(ctx.Timestamp.Kind == DateTimeKind.Utc ? "UTC" : "LOCAL")}]",

        ["unix"] = ctx => $"[Unix: {new DateTimeOffset(ctx.Timestamp).ToUnixTimeSeconds()}]",

        ["procstart"] = _ =>
        {
            var p = Process.GetCurrentProcess();
            return $"[ProcStart: {p.StartTime:O}]";
        },

        ["uptime"] = _ =>
        {
            var p = Process.GetCurrentProcess();
            return $"[Uptime: {(DateTime.Now - p.StartTime).TotalSeconds:F0}s]";
        },

        ["framework"] = _ =>
            $"[Runtime: {RuntimeInformation.FrameworkDescription}]",

        ["os"] = _ =>
            $"[OS: {RuntimeInformation.OSDescription}]",

        ["arch"] = _ =>
            $"[Arch: {RuntimeInformation.ProcessArchitecture}]",

        ["mem"] = _ => $"[Memory: {GC.GetTotalMemory(false) / 1024 / 1024}MB]",

        ["gc"] = _ => $"[GC: {GC.CollectionCount(0)}/{GC.CollectionCount(1)}/{GC.CollectionCount(2)}]",

        ["cwd"] = _ => $"[CWD: {Environment.CurrentDirectory}]",

        ["app"] = _ => $"[App: {AppDomain.CurrentDomain.FriendlyName}]",

        ["appbase"] = _ => $"[AppBase: {AppDomain.CurrentDomain.BaseDirectory}]",

        ["domain"] = _ => $"[Domain: {AppDomain.CurrentDomain.Id}]",

        ["threadname"] = _ => $"[ThreadName: {Thread.CurrentThread.Name ?? "n/a"}]",

        ["task"] = _ => $"[TaskId: {Task.CurrentId?.ToString() ?? "n/a"}]",

        ["host"] = ctx => $"[Host: {ctx.HostName}]",

        ["machine"] = _ => $"[Machine: {Environment.MachineName}]",

        ["category"] = ctx => $"[Category: {ctx.Category}]",

        ["thread"] = _ => $"[Thread: {Environment.CurrentManagedThreadId}]",

        ["process"] = _ =>
        {
            var p = Process.GetCurrentProcess();
            return $"[Process: {p.ProcessName}]";
        },

        ["pid"] = _ => $"[PID: {Process.GetCurrentProcess().Id}]",

        ["sev"] = ctx => $"[Severity: {ctx.LogType}]",

        ["logtype"] = ctx => $"[{ctx.LogType}]",

        ["user"] = _ => $"[User: {Environment.UserName}]",

        ["env"] = ctx => $"[Env: {ctx.EnvironmentName}]",

        ["newline"] = _ => Environment.NewLine
    };

    /// <summary>
    /// Gets a read-only dictionary of token resolver functions used to generate header values based on a token name and
    /// header context.
    /// </summary>
    /// <remarks>Each entry maps a token name to a function that produces the corresponding header value for a
    /// given <see cref="HeaderContext"/>. The dictionary is static and cannot be modified at runtime.</remarks>
    public IReadOnlyDictionary<string, Func<HeaderContext, string>> TokenResolvers => _tokenResolvers;

    /// <summary>
    /// Adds or overrides a custom token.
    /// </summary>
    public void AddCustomToken(string key, Func<HeaderContext, string> resolver)
    {
        if (string.IsNullOrWhiteSpace(key))
        {
            throw new ArgumentException("Token key cannot be null or whitespace.", nameof(key));
        }

        if (resolver == null)
        {
            throw new ArgumentNullException(nameof(resolver));
        }

        _tokenResolvers[key] = resolver;
    }
}
