using log4net;
using log4net.Config;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;

namespace Esim.SendMail.Logging;

public class Log4NetProvider : ILoggerProvider
{
    private readonly ConcurrentDictionary<string, Log4NetLogger> _loggers = new();

    public Log4NetProvider() { }

    public ILogger CreateLogger(string categoryName)
    {
        return _loggers.GetOrAdd(categoryName, name => new Log4NetLogger(name));
    }

    public void Dispose()
    {
        _loggers.Clear();
    }
}

public class Log4NetLogger : ILogger
{
    private readonly ILog _log;

    public Log4NetLogger(string categoryName)
    {
        _log = LogManager.GetLogger(typeof(Log4NetProvider).Assembly, categoryName);
    }

    public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;

    public bool IsEnabled(LogLevel logLevel)
    {
        return logLevel switch
        {
            LogLevel.Critical => _log.IsFatalEnabled,
            LogLevel.Error => _log.IsErrorEnabled,
            LogLevel.Warning => _log.IsWarnEnabled,
            LogLevel.Information => _log.IsInfoEnabled,
            LogLevel.Debug => _log.IsDebugEnabled,
            LogLevel.Trace => _log.IsDebugEnabled,
            _ => false
        };
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        if (!IsEnabled(logLevel)) return;

        var message = formatter(state, exception);
        if (string.IsNullOrEmpty(message) && exception == null) return;

        switch (logLevel)
        {
            case LogLevel.Critical: _log.Fatal(message, exception); break;
            case LogLevel.Error: _log.Error(message, exception); break;
            case LogLevel.Warning: _log.Warn(message, exception); break;
            case LogLevel.Information: _log.Info(message, exception); break;
            case LogLevel.Debug: _log.Debug(message, exception); break;
            case LogLevel.Trace: _log.Debug(message, exception); break;
        }
    }
}

public static class Log4NetExtensions
{
    public static ILoggingBuilder AddLog4Net(this ILoggingBuilder builder, string configPath)
    {
        XmlConfigurator.Configure(new FileInfo(configPath));
        builder.AddProvider(new Log4NetProvider());
        return builder;
    }
}
