using System;
using System.IO;
using NLog;
using NLog.Config;

namespace MusicBeePlugin.Clouseau.Core
{
    /// <summary>
    /// NLog initialization and logger provider for Clouseau plugin.
    /// Provides named loggers for different subsystems.
    /// </summary>
    public static class LogManager
    {
        private static bool _initialized = false;
        private static readonly object _lock = new object();

        // Named loggers for different subsystems
        private static Logger _coreLogger;
        private static Logger _eventsLogger;
        private static Logger _metricsLogger;
        private static Logger _apiLogger;
        private static Logger _uiLogger;
        private static Logger _performanceLogger;

        /// <summary>
        /// Core plugin operations logger
        /// </summary>
        public static Logger Core
        {
            get
            {
                EnsureInitialized();
                return _coreLogger ?? NLog.LogManager.CreateNullLogger();
            }
        }

        /// <summary>
        /// MusicBee events logger
        /// </summary>
        public static Logger Events
        {
            get
            {
                EnsureInitialized();
                return _eventsLogger ?? NLog.LogManager.CreateNullLogger();
            }
        }

        /// <summary>
        /// System metrics logger
        /// </summary>
        public static Logger Metrics
        {
            get
            {
                EnsureInitialized();
                return _metricsLogger ?? NLog.LogManager.CreateNullLogger();
            }
        }

        /// <summary>
        /// MusicBee API calls logger
        /// </summary>
        public static Logger Api
        {
            get
            {
                EnsureInitialized();
                return _apiLogger ?? NLog.LogManager.CreateNullLogger();
            }
        }

        /// <summary>
        /// UI operations logger
        /// </summary>
        public static Logger UI
        {
            get
            {
                EnsureInitialized();
                return _uiLogger ?? NLog.LogManager.CreateNullLogger();
            }
        }

        /// <summary>
        /// Performance monitoring logger
        /// </summary>
        public static Logger Performance
        {
            get
            {
                EnsureInitialized();
                return _performanceLogger ?? NLog.LogManager.CreateNullLogger();
            }
        }

        /// <summary>
        /// Get a custom named logger
        /// </summary>
        public static Logger GetLogger(string name)
        {
            EnsureInitialized();
            return NLog.LogManager.GetLogger($"Clouseau.{name}");
        }

        /// <summary>
        /// Initialize NLog from configuration file
        /// </summary>
        public static void Initialize(string dataPath)
        {
            lock (_lock)
            {
                if (_initialized) return;

                try
                {
                    // Create logs directory
                    var logsPath = Path.Combine(dataPath, "logs");
                    if (!Directory.Exists(logsPath))
                        Directory.CreateDirectory(logsPath);

                    // Look for NLog.config in plugin directory (next to the DLL)
                    var pluginDir = Path.GetDirectoryName(typeof(LogManager).Assembly.Location);
                    var configPath = Path.Combine(pluginDir, "NLog.config");

                    if (File.Exists(configPath))
                    {
                        // Load from config file
                        NLog.LogManager.Configuration = new XmlLoggingConfiguration(configPath);
                    }
                    else
                    {
                        // Create programmatic configuration as fallback
                        CreateDefaultConfiguration(logsPath);
                    }

                    // Create named loggers
                    _coreLogger = NLog.LogManager.GetLogger("Clouseau.Core");
                    _eventsLogger = NLog.LogManager.GetLogger("Clouseau.Events");
                    _metricsLogger = NLog.LogManager.GetLogger("Clouseau.Metrics");
                    _apiLogger = NLog.LogManager.GetLogger("Clouseau.Api");
                    _uiLogger = NLog.LogManager.GetLogger("Clouseau.UI");
                    _performanceLogger = NLog.LogManager.GetLogger("Clouseau.Performance");

                    _initialized = true;

                    _coreLogger.Info("LogManager initialized successfully");
                    _coreLogger.Debug($"NLog config path: {configPath}");
                    _coreLogger.Debug($"Logs directory: {logsPath}");
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine($"Failed to initialize NLog: {ex}");
                    CreateDefaultConfiguration(Path.Combine(dataPath, "logs"));
                    _initialized = true;
                }
            }
        }

        /// <summary>
        /// Create default NLog configuration programmatically
        /// </summary>
        private static void CreateDefaultConfiguration(string logsPath)
        {
            var config = new LoggingConfiguration();

            // Main log file target
            var mainLogTarget = new NLog.Targets.FileTarget("mainLog")
            {
                FileName = Path.Combine(logsPath, "clouseau.log"),
                Layout = "${longdate}|${level:uppercase=true:padding=-5}|${logger:shortName=true}|${message}${onexception:inner=${newline}${exception:format=tostring}}",
                ArchiveFileName = Path.Combine(logsPath, "archive", "clouseau.{#}.log"),
                ArchiveEvery = NLog.Targets.FileArchivePeriod.Day,
                ArchiveNumbering = NLog.Targets.ArchiveNumberingMode.DateAndSequence,
                MaxArchiveFiles = 30,
                ConcurrentWrites = true,
                KeepFileOpen = true,
                Encoding = System.Text.Encoding.UTF8
            };

            // Events log file target
            var eventsLogTarget = new NLog.Targets.FileTarget("eventsLog")
            {
                FileName = Path.Combine(logsPath, "events.log"),
                Layout = "${longdate}|${event-properties:item=Category}|${event-properties:item=EventType}|${message}",
                ArchiveFileName = Path.Combine(logsPath, "archive", "events.{#}.log"),
                ArchiveEvery = NLog.Targets.FileArchivePeriod.Day,
                MaxArchiveFiles = 14,
                ConcurrentWrites = true,
                KeepFileOpen = true,
                Encoding = System.Text.Encoding.UTF8
            };

            // Error log file target
            var errorLogTarget = new NLog.Targets.FileTarget("errorLog")
            {
                FileName = Path.Combine(logsPath, "errors.log"),
                Layout = "${longdate}|${level:uppercase=true}|${logger}|${message}${newline}${exception:format=tostring}",
                ArchiveFileName = Path.Combine(logsPath, "archive", "errors.{#}.log"),
                ArchiveEvery = NLog.Targets.FileArchivePeriod.Day,
                MaxArchiveFiles = 30,
                ConcurrentWrites = true,
                KeepFileOpen = true,
                Encoding = System.Text.Encoding.UTF8
            };

            // Debugger target
            var debuggerTarget = new NLog.Targets.DebuggerTarget("debugger")
            {
                Layout = "${time}|${level:padding=-5}|${message}"
            };

            // Add targets to configuration
            config.AddTarget(mainLogTarget);
            config.AddTarget(eventsLogTarget);
            config.AddTarget(errorLogTarget);
            config.AddTarget(debuggerTarget);

            // Add rules
            config.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, mainLogTarget);
            config.AddRule(NLog.LogLevel.Info, NLog.LogLevel.Fatal, eventsLogTarget, "Clouseau.Events");
            config.AddRule(NLog.LogLevel.Error, NLog.LogLevel.Fatal, errorLogTarget);
            config.AddRule(NLog.LogLevel.Debug, NLog.LogLevel.Fatal, debuggerTarget);

            // Apply configuration
            NLog.LogManager.Configuration = config;

            // Create loggers
            _coreLogger = NLog.LogManager.GetLogger("Clouseau.Core");
            _eventsLogger = NLog.LogManager.GetLogger("Clouseau.Events");
            _metricsLogger = NLog.LogManager.GetLogger("Clouseau.Metrics");
            _apiLogger = NLog.LogManager.GetLogger("Clouseau.Api");
            _uiLogger = NLog.LogManager.GetLogger("Clouseau.UI");
            _performanceLogger = NLog.LogManager.GetLogger("Clouseau.Performance");
        }

        /// <summary>
        /// Ensure logging is initialized before use
        /// </summary>
        private static void EnsureInitialized()
        {
            if (!_initialized)
            {
                // Fallback initialization with temp path
                var tempPath = Path.Combine(Path.GetTempPath(), "mb_clouseau");
                Initialize(tempPath);
            }
        }

        /// <summary>
        /// Shutdown NLog and flush all pending log entries
        /// </summary>
        public static void Shutdown()
        {
            lock (_lock)
            {
                if (_initialized)
                {
                    try
                    {
                        _coreLogger?.Info("LogManager shutting down");
                        NLog.LogManager.Shutdown();
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine($"Error during NLog shutdown: {ex.Message}");
                    }
                    finally
                    {
                        _initialized = false;
                        _coreLogger = null;
                        _eventsLogger = null;
                        _metricsLogger = null;
                        _apiLogger = null;
                        _uiLogger = null;
                        _performanceLogger = null;
                    }
                }
            }
        }

        /// <summary>
        /// Flush all pending log entries
        /// </summary>
        public static void Flush()
        {
            try
            {
                NLog.LogManager.Flush();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Error during NLog flush: {ex.Message}");
            }
        }

        /// <summary>
        /// Log a performance metric
        /// </summary>
        public static void LogPerformance(string operation, long durationMs, string details = null)
        {
            var logEvent = new LogEventInfo(NLog.LogLevel.Info, "Clouseau.Performance", details ?? operation);
            logEvent.Properties["Operation"] = operation;
            logEvent.Properties["DurationMs"] = durationMs;
            logEvent.Properties["Details"] = details ?? "";
            Performance.Log(logEvent);
        }

        /// <summary>
        /// Log an API call
        /// </summary>
        public static void LogApiCall(string methodName, long durationMs, string result = null)
        {
            var logEvent = new LogEventInfo(NLog.LogLevel.Debug, "Clouseau.Api", result ?? methodName);
            logEvent.Properties["ApiMethod"] = methodName;
            logEvent.Properties["Duration"] = durationMs;
            Api.Log(logEvent);
        }
    }
}
