using EonaCat.LogStack.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
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>
/// Factory for creating logger instances with a shared configuration
/// </summary>
public interface ILoggerFactory : IAsyncDisposable
{
    /// <summary>
    /// Creates or retrieves a logger for the specified category
    /// </summary>
    ILogger CreateLogger(string categoryName);

    /// <summary>
    /// Gets the underlying EonaCatLogStack instance
    /// </summary>
    EonaCatLogStack GetLogStack();

    /// <summary>
    /// Flushes all pending log events
    /// </summary>
    System.Threading.Tasks.Task FlushAsync();

    /// <summary>
    /// Gets diagnostics information about the logger
    /// </summary>
    LoggerDiagnostics GetDiagnostics();

    /// <summary>
    /// Gets detailed metrics about the logger
    /// </summary>
    LoggerMetrics GetMetrics();
}

/// <summary>
/// Default implementation of ILoggerFactory that manages a single EonaCatLogStack instance
/// </summary>
public sealed class LoggerFactory : ILoggerFactory
{
    private readonly EonaCatLogStack _logStack;
    private readonly ConcurrentDictionary<string, Logger> _loggers;
    private volatile bool _isDisposed;

    /// <summary>
    /// Creates a new LoggerFactory with default settings
    /// </summary>
    public LoggerFactory(
        LogLevel minimumLevel = LogLevel.Trace,
        TimestampMode timestampMode = TimestampMode.Utc)
    {
        _logStack = new EonaCatLogStack(
            category: "LoggerFactory",
            minimumLevel: minimumLevel,
            timestampMode: timestampMode);
        _loggers = new ConcurrentDictionary<string, Logger>();
    }

    /// <summary>
    /// Creates a new LoggerFactory using an existing EonaCatLogStack instance
    /// </summary>
    public LoggerFactory(EonaCatLogStack logStack)
    {
        _logStack = logStack ?? throw new ArgumentNullException(nameof(logStack));
        _loggers = new ConcurrentDictionary<string, Logger>();
    }

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

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

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

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

        return _logStack;
    }

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

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

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

        return _logStack.GetDiagnostics();
    }

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

        return _logStack.GetMetrics();
    }

    /// <summary>
    /// Disposes all loggers and the underlying log stack
    /// </summary>
    public async ValueTask DisposeAsync()
    {
        if (_isDisposed)
        {
            return;
        }

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

    /// <summary>
    /// Internal logger adapter that wraps EonaCatLogStack
    /// </summary>
    private sealed class Logger : ILogger
    {
        private readonly string _category;
        private readonly EonaCatLogStack _logStack;

        public string Category => _category;

        public Logger(string category, EonaCatLogStack logStack)
        {
            _category = category ?? "Default";
            _logStack = logStack;
        }

        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
            {
                // Fallback if formatting fails
                _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
            {
                // Fallback if formatting fails
                _logStack.Log(level, exception, format);
            }
        }

        public bool IsEnabled(LogLevel level)
        {
            // Check if level is enabled in the logstack
            // This is a simple check; could be enhanced
            return level >= LogLevel.Trace;
        }
    }
}
