using System;
using Deploymentcenter.Client;
using PolyTrader.Core;
using PolyTrader.Core.Configuration;
using PolyTrader.Core.Security;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;
namespace PolyTrader.App.Avalonia.Licensing
{
///
/// Lizenz-Torwächter für den Programmstart (D-2). Ersetzt den früheren, gegen
/// LicenseLabrador.Client gebauten Licensing/LicenseGate.cs im WinForms-Projekt
/// (siehe docs/archiv/umsetzungsplaene/UMSETZUNGSPLAN-Deploymentcenter-Integration.md) – dort läuft
/// ohnehin keine Lizenzprüfung, weil Avalonia die tatsächlich benutzte Anwendung ist.
///
/// **Grundsatz unverändert:** kein bei ungültiger Lizenz. Ein
/// Trading-Bot mit offenen Positionen darf nicht mitten im Lauf abgeschossen werden. Bei
/// fehlender/ungültiger Lizenz startet die Core-Shell (Terminal, Einstellungen) ohne die
/// Trading-/Analyse-Module; der Schlüssel lässt sich jederzeit über den Button
/// „Lizenzschlüssel setzen …" im Einstellungsfenster nachtragen (wirkt ab dem nächsten Start –
/// eine Laufzeit-Revalidierung gibt es bewusst nicht, siehe Entscheidung vom 31.07.2026).
///
/// **Bewusst kein eigenes :** Der interaktive Weg läuft über die
/// bereits vorhandene, erprobte Settings-UI (derselbe maskierte Prompt-Button wie beim
/// Watchdog-Token); ein zusätzlicher Dialog nur für die Lizenz wäre eine zweite, redundante
/// Eingabe-Oberfläche. Für den Kopflos-Betrieb (--headless, CLI-Schalter) genügt die vom
/// SDK mitgelieferte – eine eigene Fassung böte keinen Mehrwert.
///
public static class LicenseGate
{
///
/// Prüft still (ohne Dialog), ob die gespeicherte Lizenz aktuell nutzbar ist. Entscheidet
/// darüber, ob die Trading-/Analyse-Module überhaupt registriert werden – muss deshalb vor
/// dem Host-Aufbau laufen. Wirft nie: jeder Fehler in der Prüfung führt zum eingeschränkten
/// Modus, nicht zum Absturz.
///
public static bool CheckUsable(DeploymentcenterOptions options, ServerSettings settings, TerminalLogger logger)
{
try
{
if (string.IsNullOrWhiteSpace(options.BaseUrl))
{
logger.Warning("⚠️ Keine Deploymentcenter-URL konfiguriert. Start im eingeschränkten Modus (nur Core-Shell).");
return false;
}
// Befund D-3 aus dem Umsetzungsplan: das SDK erzwingt selbst kein TLS. Ohne diese
// Prüfung ginge der Lizenzschlüssel im Klartext übers Netz, wenn die URL versehentlich
// auf http:// steht.
if (!Uri.TryCreate(options.BaseUrl, UriKind.Absolute, out var parsed) ||
!(parsed.Scheme == Uri.UriSchemeHttps || (parsed.Scheme == Uri.UriSchemeHttp && parsed.IsLoopback)))
{
logger.Warning($"⚠️ Deploymentcenter-URL muss HTTPS verwenden (http:// nur für localhost): '{options.BaseUrl}'. " +
"Start im eingeschränkten Modus.");
return false;
}
string key;
try
{
// Erlaubt einen mit dem Master-Key verschlüsselten Schlüssel (enc:v1:…);
// Klartext wird unverändert durchgereicht.
key = SecretProtection.Unprotect(settings.LicenseKey);
}
catch (Exception ex)
{
logger.Warning($"⚠️ Gespeicherter Lizenzschlüssel nicht lesbar ({ex.Message}) – behandle als 'keine Lizenz'.");
return false;
}
if (string.IsNullOrWhiteSpace(key))
{
logger.Warning("⚠️ Kein Lizenzschlüssel gesetzt. Start im eingeschränkten Modus (nur Core-Shell). " +
"Schlüssel über „Lizenzschlüssel setzen …\" im Einstellungsfenster eintragen.");
return false;
}
LicenseClient.DefaultAppVersion = BuildInfo.Version;
var client = new LicenseClient();
LicenseValidationResult result = client
.ValidateAsync(options.ProjectSlug, key, options.BaseUrl, BuildInfo.Version)
.GetAwaiter().GetResult();
if (result.IsValid)
{
logger.Info(result.IsCached
? $"🔑 Lizenz im Offline-Kulanzmodus gültig ({result.Status})."
: "🔑 Lizenz gültig (online geprüft).");
return true;
}
logger.Warning($"⚠️ Keine gültige Lizenz ({result.Status}: {result.Message}). " +
"Start im eingeschränkten Modus (nur Core-Shell, keine Module). " +
"Schlüssel über „Lizenzschlüssel setzen …\" im Einstellungsfenster prüfen.");
return false;
}
catch (Exception ex)
{
logger.Error($"❌ Lizenzprüfung unerwartet fehlgeschlagen: {ex.Message}. Start im eingeschränkten Modus.");
return false;
}
}
}
}