using System;
using LicenseLabrador.Client;
using PolyTrader.Core.Security;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;

namespace PolyTraderSharp.Licensing
{
    /// <summary>
    /// Lizenz-Torwächter für den Programmstart (B.4). Prüft beim Start die LicenseLabrador-Lizenz und
    /// entscheidet, ob PolyTrader voll (mit allen Modulen) oder eingeschränkt (nur Core-Shell, damit
    /// Terminal und Einstellungen zum Eintragen einer Lizenz erreichbar bleiben) startet.
    ///
    /// **Grundsatz:** kein harter Abbruch (<c>Environment.Exit</c>) bei ungültiger Lizenz. Ein
    /// Trading-Bot mit offenen Positionen darf nicht mitten im Lauf hart abgeschossen werden – im
    /// Zweifel startet die Shell ohne die Trading-/Analyse-Module weiter. Der Gate wirft nie; jeder
    /// Fehler in der Prüfung führt zum eingeschränkten Modus, nicht zum Absturz.
    ///
    /// Die eigentliche Kryptografie (Ed25519-Signaturprüfung, Nonce, Offline-Kulanz, Hardware-Bindung)
    /// liegt im <see cref="LicenseClient"/> des SDK; dieser Gate ist nur die Anwendungslogik drumherum.
    /// </summary>
    public static class LicenseGate
    {
        /// <summary>
        /// Öffentlicher Ed25519-Schlüssel des Lizenzservers (license.mhdf.de), Base64. Bewusst fest
        /// eingebettet: nur mit diesem Schlüssel lässt sich eine Server-Antwort verifizieren – ein
        /// Angreifer kann ohne den (nur serverseitigen) privaten Schlüssel kein „valid" fälschen.
        /// </summary>
        private const string PublicKeyBase64 = "L7YR1wMKk8+lNefatzL+DMvAtHFVkZWYXAxXGrro+/U=";

        /// <summary>Produkt-Slug im LicenseLabrador-Admin.</summary>
        private const string ProductSlug = "pt";

        /// <summary>
        /// Lizenzserver-Endpunkt. Ausschließlich HTTPS – über http:// gingen Lizenzschlüssel und
        /// Hardware-ID im Klartext übers Netz (die signierte Antwort bliebe zwar fälschungssicher,
        /// der Schlüssel würde aber geleakt).
        /// </summary>
        private static readonly string[] Endpoints = { "https://license.mhdf.de" };

        /// <summary>Offline-Kulanz, falls der Server das Feld nicht liefert (7 Tage).</summary>
        private const int OfflineGraceHoursFallback = 168;

        public static LicenseConfig BuildConfig() => new LicenseConfig
        {
            ProductSlug = ProductSlug,
            PublicKeyBase64 = PublicKeyBase64,
            Endpoints = Endpoints,
            OfflineGraceHoursFallback = OfflineGraceHoursFallback
        };

        /// <summary>
        /// Führt die Startprüfung durch. Rückgabe <c>true</c> = App darf voll (mit Modulen) starten.
        /// Zeigt bei ungültiger Lizenz einen modalen Dialog (Schlüssel eingeben, Hardware-ID anzeigen,
        /// eingeschränkt starten). Wirft nie.
        /// </summary>
        public static bool RunStartupGate(ServerSettings settings, TerminalLogger logger, string settingsPath)
        {
            LicenseClient client;
            try
            {
                client = new LicenseClient(BuildConfig());
            }
            catch (Exception ex)
            {
                logger.Error($"❌ Lizenzprüfung nicht initialisierbar: {ex.Message}. Start im eingeschränkten Modus.");
                return false;
            }

            string storedKey = "";
            try
            {
                // Erlaubt einen mit dem Master-Key verschlüsselten Schlüssel (enc:v1:…); Klartext
                // wird unverändert durchgereicht.
                storedKey = SecretProtection.Unprotect(settings.LicenseKey);
            }
            catch (Exception ex)
            {
                logger.Warning($"⚠️ Gespeicherter Lizenzschlüssel nicht lesbar ({ex.Message}) – behandle als 'keine Lizenz'.");
            }

            LicenseResult result = EvaluateBlocking(client, storedKey);

            if (result.IsUsable)
            {
                LogUsable(result, logger);
                return true;
            }

            logger.Warning($"⚠️ Keine gültige Lizenz: {Describe(result)}. Öffne Lizenzdialog.");

            LicenseResult? dialogResult;
            string? dialogKey;
            try
            {
                using var dlg = new PolyTraderSharp.Ui.LicenseDialog(client, storedKey, result);
                dlg.ShowDialog();
                dialogResult = dlg.ValidatedResult;
                dialogKey = dlg.ValidatedKey;
            }
            catch (Exception ex)
            {
                logger.Error($"❌ Lizenzdialog fehlgeschlagen: {ex.Message}. Start im eingeschränkten Modus.");
                return false;
            }

            if (dialogResult != null && dialogResult.IsUsable && !string.IsNullOrWhiteSpace(dialogKey))
            {
                try
                {
                    // Bei gesetztem Master-Key verschlüsselt speichern; sonst Klartext (Passthrough).
                    settings.LicenseKey = SecretProtection.Protect(dialogKey!.Trim());
                    settings.Save(settingsPath);
                    logger.Info("🔑 Lizenz validiert und gespeichert.");
                }
                catch (Exception ex)
                {
                    logger.Error($"Lizenz gültig, aber Speichern des Schlüssels fehlgeschlagen: {ex.Message}.");
                }

                LogUsable(dialogResult, logger);
                return true;
            }

            logger.Warning(
                "⚠️ Start im EINGESCHRÄNKTEN Modus – keine Module aktiv, nur Terminal und Einstellungen. " +
                "Lizenz in den Einstellungen setzen und PolyTrader neu starten.");
            return false;
        }

        /// <summary>
        /// Validiert synchron (blockierend). Das SDK arbeitet durchgängig mit ConfigureAwait(false),
        /// daher ist das Blockieren vor der Message-Loop unbedenklich. Ein leerer Schlüssel ergibt
        /// planmäßig <see cref="LicenseState.NoLicense"/>.
        /// </summary>
        internal static LicenseResult EvaluateBlocking(LicenseClient client, string? key)
        {
            return client.ValidateAsync(key ?? string.Empty).GetAwaiter().GetResult();
        }

        private static void LogUsable(LicenseResult result, TerminalLogger logger)
        {
            switch (result.State)
            {
                case LicenseState.Valid:
                    logger.Info("🔑 Lizenz gültig (online geprüft).");
                    break;
                case LicenseState.ValidOffline:
                    logger.Warning($"🔑 Lizenz im Offline-Kulanzmodus gültig bis {result.GraceUntil:g} " +
                                   "(Server nicht erreichbar).");
                    break;
                case LicenseState.ValidLocalFile:
                    logger.Info("🔑 Lizenz über lokale Offline-Lizenzdatei gültig.");
                    break;
                default:
                    logger.Info($"🔑 Lizenz nutzbar ({result.State}).");
                    break;
            }
        }

        /// <summary>Kurzbeschreibung eines nicht nutzbaren Ergebnisses für das Log/den Dialog.</summary>
        internal static string Describe(LicenseResult result)
        {
            return result.State switch
            {
                LicenseState.NoLicense       => "kein oder ungültiger Schlüssel / Server nicht erreichbar",
                LicenseState.NotFound        => "Schlüssel dem Server unbekannt",
                LicenseState.Revoked         => "Lizenz wurde widerrufen",
                LicenseState.Expired         => "Lizenz abgelaufen",
                LicenseState.ActivationLimit => "maximale Aktivierungen erreicht",
                LicenseState.TamperSuspected => "Manipulation erkannt (Signatur/Uhr) – Prüfung verweigert",
                _                            => result.State.ToString()
            };
        }
    }
}
