using EonaCat.LogStack.Boosters;
using EonaCat.LogStack.Core;
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

namespace EonaCat.LogStack.Logging;

// 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>
/// Advanced configuration for category-specific logger settings
/// </summary>
public class CategoryConfig
{
    public string CategoryName { get; set; } = "";
    public LogLevel? MinimumLevel { get; set; }
    public List<IBooster> Boosters { get; set; } = new();
    public Dictionary<string, object> Properties { get; set; } = new();
    public Func<LogEventBuilder, bool>? Filter { get; set; }
}

/// <summary>
/// Enhanced LoggerFactory with category configuration, dynamic levels, and context propagation
/// </summary>
public sealed class AdvancedLoggerFactory : ILoggerFactory
{
    private readonly EonaCatLogStack _logStack;
    private readonly ConcurrentDictionary<string, Logger> _loggers;
    private readonly ConcurrentDictionary<string, CategoryConfig> _categoryConfigs;
    private readonly AsyncLocal<Dictionary<string, object>> _contextData;
    private volatile bool _isDisposed;

    public AdvancedLoggerFactory(
        LogLevel minimumLevel = LogLevel.Trace,
        TimestampMode timestampMode = TimestampMode.Utc)
    {
        _logStack = new EonaCatLogStack(
            category: "AdvancedFactory",
            minimumLevel: minimumLevel,
            timestampMode: timestampMode);
        _loggers = new ConcurrentDictionary<string, Logger>();
        _categoryConfigs = new ConcurrentDictionary<string, CategoryConfig>();
        _contextData = new AsyncLocal<Dictionary<string, object>>();
    }

    /// <summary>
    /// Configures settings for a specific category
    /// </summary>
    public AdvancedLoggerFactory ConfigureCategory(string categoryName, Action<CategoryConfig> configure)
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(AdvancedLoggerFactory));
        }

        var config = _categoryConfigs.GetOrAdd(categoryName, _ => new CategoryConfig { CategoryName = categoryName });
        configure(config);
        return this;
    }

    /// <summary>
    /// Creates or retrieves a logger for the specified category
    /// </summary>
    public ILogger CreateLogger(string categoryName)
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(AdvancedLoggerFactory));
        }

        if (string.IsNullOrEmpty(categoryName))
        {
            categoryName = "Default";
        }

        return _loggers.GetOrAdd(categoryName, name => new Logger(name, _logStack, _categoryConfigs));
    }

    /// <summary>
    /// Gets the underlying EonaCatLogStack instance
    /// </summary>
    public EonaCatLogStack GetLogStack()
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(AdvancedLoggerFactory));
        }

        return _logStack;
    }

    /// <summary>
    /// Sets context data that will be included in all logs within this async context
    /// </summary>
    public void SetContextData(string key, object value)
    {
        var ctx = _contextData.Value ??= new Dictionary<string, object>();
        ctx[key] = value;
    }

    /// <summary>
    /// Gets context data for the current async context
    /// </summary>
    public object? GetContextData(string key)
    {
        var ctx = _contextData.Value;
        if (ctx == null)
        {
            return null;
        }

        ctx.TryGetValue(key, out var value);
        return value;
    }

    /// <summary>
    /// Clears all context data for the current async context
    /// </summary>
    public void ClearContextData()
    {
        _contextData.Value?.Clear();
    }

    /// <summary>
    /// Dynamically changes the minimum log level at runtime
    /// </summary>
    public void SetDynamicLevel(LogLevel level, string? categoryName = null)
    {
        if (categoryName != null)
        {
            if (_categoryConfigs.TryGetValue(categoryName, out var config))
            {
                config.MinimumLevel = level;
            }
        }
        else
        {
            // Update all categories
            foreach (var config in _categoryConfigs.Values)
            {
                config.MinimumLevel = level;
            }
        }
    }

    /// <summary>
    /// Flushes all pending log events
    /// </summary>
    public async Task FlushAsync()
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(AdvancedLoggerFactory));
        }

        await _logStack.FlushAsync().ConfigureAwait(false);
    }

    /// <summary>
    /// Gets diagnostics information
    /// </summary>
    public LoggerDiagnostics GetDiagnostics()
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(AdvancedLoggerFactory));
        }

        var baseDiagnostics = _logStack.GetDiagnostics();
        return new LoggerDiagnostics
        {
            Category = "AdvancedFactory",
            MinimumLevel = baseDiagnostics.MinimumLevel,
            TotalLogged = baseDiagnostics.TotalLogged,
            TotalDropped = baseDiagnostics.TotalDropped,
            TotalExceptions = baseDiagnostics.TotalExceptions,
            FlowCount = baseDiagnostics.FlowCount,
            BoosterCount = baseDiagnostics.BoosterCount,
            Flows = baseDiagnostics.Flows
        };
    }

    /// <summary>
    /// Gets factory-specific diagnostics
    /// </summary>
    public FactoryDiagnostics GetFactoryDiagnostics()
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(AdvancedLoggerFactory));
        }

        return new FactoryDiagnostics
        {
            ActiveLoggers = _loggers.Count,
            ConfiguredCategories = _categoryConfigs.Count,
            TotalLogged = _logStack.GetDiagnostics().TotalLogged,
            TotalDropped = _logStack.GetDiagnostics().TotalDropped
        };
    }

    /// <summary>
    /// Gets detailed metrics
    /// </summary>
    public LoggerMetrics GetMetrics()
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(AdvancedLoggerFactory));
        }

        return _logStack.GetMetrics();
    }

    public async ValueTask DisposeAsync()
    {
        if (_isDisposed)
        {
            return;
        }

        _isDisposed = true;
        _loggers.Clear();
        _categoryConfigs.Clear();
        _contextData.Value?.Clear();
        await _logStack.DisposeAsync().ConfigureAwait(false);
        GC.SuppressFinalize(this);
    }

    /// <summary>
    /// Internal logger adapter with category support
    /// </summary>
    private sealed class Logger : ILogger
    {
        private readonly string _category;
        private readonly EonaCatLogStack _logStack;
        private readonly ConcurrentDictionary<string, CategoryConfig> _configs;

        public string Category => _category;

        public Logger(string category, EonaCatLogStack logStack, ConcurrentDictionary<string, CategoryConfig> configs)
        {
            _category = category ?? "Default";
            _logStack = logStack;
            _configs = configs;
        }

        private CategoryConfig? GetCategoryConfig()
        {
            _configs.TryGetValue(_category, out var config);
            return config;
        }

        private bool ShouldLog(LogLevel level)
        {
            var config = GetCategoryConfig();
            if (config?.MinimumLevel.HasValue == true)
            {
                return level >= config.MinimumLevel;
            }
            return true;
        }

        public void Log(LogLevel level, string message)
        {
            if (!IsEnabled(level))
            {
                return;
            }

            _logStack.Log(message, level);
        }

        public void Log(LogLevel level, Exception? exception, string message)
        {
            if (!IsEnabled(level))
            {
                return;
            }

            _logStack.Log(level, exception, message);
        }

        public void Log(LogLevel level, string format, params object[] args)
        {
            if (!IsEnabled(level))
            {
                return;
            }

            try
            {
                var message = string.Format(format, args);
                _logStack.Log(message, level);
            }
            catch
            {
                _logStack.Log(format, level);
            }
        }

        public void Log(LogLevel level, Exception? exception, string format, params object[] args)
        {
            if (!IsEnabled(level))
            {
                return;
            }

            try
            {
                var message = string.Format(format, args);
                _logStack.Log(level, exception, message);
            }
            catch
            {
                _logStack.Log(level, exception, format);
            }
        }

        public bool IsEnabled(LogLevel level)
        {
            return ShouldLog(level);
        }
    }
}

/// <summary>
/// Diagnostics information from AdvancedLoggerFactory
/// </summary>
public class FactoryDiagnostics
{
    public int ActiveLoggers { get; set; }
    public int ConfiguredCategories { get; set; }
    public long TotalLogged { get; set; }
    public long TotalDropped { get; set; }
}
