using Predictalytics.Infrastructure.Logging;
using Serilog;
using Serilog.Events;

namespace Predictalytics.Hosting;

/// <summary>
/// Serilog-Konfiguration der Anwendung. Plattformneutral: der optionale
/// Terminal-Sink bekommt lediglich eine Schreib-Action, das Marshalling auf den
/// UI-Thread liegt beim jeweiligen Host.
/// </summary>
public static class LoggingSetup
{
    private const string TextTemplate =
        "[{Timestamp:yyyy-MM-dd HH:mm:ss}] [{Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}";

    private const string SimpleTemplate =
        "[{Timestamp:HH:mm:ss}] [{Level:u3}] {Message:lj}{NewLine}{Exception}";

    /// <summary>Umgebungsvariable, mit der das Logverzeichnis festgelegt werden kann.</summary>
    public const string LogDirectoryVariable = "PREDICTALYTICS_LOG_DIR";

    /// <summary>
    /// Standard-Logverzeichnis: neben den Einstellungen, also
    /// <c>%APPDATA%\Predictalytics\logs</c> bzw. <c>~/.config/Predictalytics/logs</c>.
    /// <para>
    /// Bewusst nicht mehr neben der Programmdatei: deren Verzeichnis ist unter Linux
    /// ueblicherweise nicht beschreibbar, und beim Update wuerde es ueberschrieben.
    /// Ueber <see cref="LogDirectoryVariable"/> uebersteuerbar — etwa auf
    /// <c>/var/log/predictalytics</c> im systemd-Betrieb.
    /// </para>
    /// </summary>
    public static string DefaultLogDirectory
    {
        get
        {
            var configured = Environment.GetEnvironmentVariable(LogDirectoryVariable);
            return string.IsNullOrWhiteSpace(configured)
                ? Path.Combine(PredictalyticsOptions.SettingsDirectory, "logs")
                : configured;
        }
    }

    /// <summary>
    /// Richtet den globalen Serilog-Logger ein.
    /// </summary>
    /// <param name="terminalSink">
    /// Optionale Schreib-Action fuer eine Terminalanzeige im Host. Bekommt die
    /// fertig formatierte Zeile und den Level; muss selbst auf den UI-Thread wechseln.
    /// </param>
    /// <param name="logDirectory">Basisverzeichnis der Logdateien. Standard: <see cref="DefaultLogDirectory"/>.</param>
    public static void Configure(
        Action<string, LogEventLevel>? terminalSink = null,
        string? logDirectory = null)
    {
        var logBaseDir = logDirectory ?? DefaultLogDirectory;

        var config = new LoggerConfiguration()
            .MinimumLevel.Information()
            .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
            .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
            .MinimumLevel.Override("Microsoft.Hosting", LogEventLevel.Warning)
            .Enrich.FromLogContext()

            // Suppress duplicate entry EF errors completely from logging
            .Filter.ByExcluding(e => e.Exception != null && e.Exception.ToString().Contains("Duplicate entry"))

            // ── Console (simple) ──
            .WriteTo.Console(outputTemplate: SimpleTemplate, restrictedToMinimumLevel: LogEventLevel.Warning)

            // ── Deployment Center error stream (Error/Fatal) ──
            // Wird hier registriert, lange bevor die Einstellungen bekannt sind: der Sink
            // fragt DcErrorReporter zur Sendezeit, ob das Melden eingeschaltet ist.
            .WriteTo.Sink(new DcErrorSink(), restrictedToMinimumLevel: LogEventLevel.Error);

        // ── Terminalanzeige des Hosts (optional) ──
        if (terminalSink != null)
        {
            config = config.WriteTo.Sink(new DelegateSink(terminalSink), restrictedToMinimumLevel: LogEventLevel.Warning);
        }

        config = config
            // ══════════════════════════════════════════════
            //  FILE SINKS — By Level
            // ══════════════════════════════════════════════

            // ALL levels — complete log (daily rotation)
            .WriteTo.File(
                Path.Combine(logBaseDir, "all", "all-.log"),
                rollingInterval: RollingInterval.Day,
                outputTemplate: TextTemplate,
                retainedFileCountLimit: 30,
                fileSizeLimitBytes: 50_000_000,
                shared: true)

            // INFO only
            .WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Information)
                .WriteTo.File(
                    Path.Combine(logBaseDir, "info", "info-.log"),
                    rollingInterval: RollingInterval.Day,
                    outputTemplate: TextTemplate,
                    retainedFileCountLimit: 14,
                    shared: true))

            // WARNING only
            .WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Warning)
                .WriteTo.File(
                    Path.Combine(logBaseDir, "warning", "warning-.log"),
                    rollingInterval: RollingInterval.Day,
                    outputTemplate: TextTemplate,
                    retainedFileCountLimit: 30,
                    shared: true))

            // ERROR + FATAL
            .WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(e => e.Level >= LogEventLevel.Error)
                .WriteTo.File(
                    Path.Combine(logBaseDir, "error", "error-.log"),
                    rollingInterval: RollingInterval.Day,
                    outputTemplate: TextTemplate,
                    retainedFileCountLimit: 60,
                    shared: true))

            // ══════════════════════════════════════════════
            //  FILE SINKS — By Platform
            // ══════════════════════════════════════════════
            .WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(e => HasPlatform(e, "Polymarket"))
                .WriteTo.File(
                    Path.Combine(logBaseDir, "platforms", "polymarket-.log"),
                    rollingInterval: RollingInterval.Day,
                    outputTemplate: TextTemplate,
                    retainedFileCountLimit: 30,
                    shared: true))

            .WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(e => HasPlatform(e, "Limitless"))
                .WriteTo.File(
                    Path.Combine(logBaseDir, "platforms", "limitless-.log"),
                    rollingInterval: RollingInterval.Day,
                    outputTemplate: TextTemplate,
                    retainedFileCountLimit: 30,
                    shared: true))

            .WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(e => HasPlatform(e, "Azuro"))
                .WriteTo.File(
                    Path.Combine(logBaseDir, "platforms", "azuro-.log"),
                    rollingInterval: RollingInterval.Day,
                    outputTemplate: TextTemplate,
                    retainedFileCountLimit: 30,
                    shared: true))

            // ══════════════════════════════════════════════
            //  FILE SINK — Worker / Discovery / Scoring
            // ══════════════════════════════════════════════
            .WriteTo.Logger(lc => lc
                .Filter.ByIncludingOnly(e =>
                    e.Properties.ContainsKey("SourceContext") &&
                    e.Properties["SourceContext"].ToString().Contains("Worker"))
                .WriteTo.File(
                    Path.Combine(logBaseDir, "workers", "workers-.log"),
                    rollingInterval: RollingInterval.Day,
                    outputTemplate: TextTemplate,
                    retainedFileCountLimit: 14,
                    shared: true));

        Log.Logger = config.CreateLogger();
    }

    private static bool HasPlatform(LogEvent e, string platform) =>
        e.Properties.ContainsKey("Platform") &&
        e.Properties["Platform"].ToString().Contains(platform);

    /// <summary>Startbanner, identisch fuer alle Hosts.</summary>
    public static void LogStartupBanner()
    {
        Log.Warning("══════════════════════════════════════════════════════");
        Log.Warning("  🚀 Predictalytics v{Version} — Data retrieval started!", DcConfig.AppVersion);
        Log.Warning("  📊 First platform report in 5 minutes.");
        Log.Warning("══════════════════════════════════════════════════════");
    }
}
