using System.ComponentModel;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Predictalytics.Hosting;

/// <summary>
/// Laufzeit-Konfiguration der Anwendung.
/// <para>
/// Die <see cref="System.ComponentModel"/>-Attribute sind plattformneutral (Teil der
/// Basisbibliothek). Die Avalonia-Shell nutzt sie fuer Beschriftungen und Hilfetexte
/// der Einstellungsansicht.
/// </para>
/// </summary>
public sealed class PredictalyticsOptions
{
    private const string FileName = "settings.json";

    // ─── Webserver ───

    [Category("Webserver")]
    [DisplayName("Port")]
    [Description("Der Port, über den die WebUI und API erreichbar sind.")]
    [DefaultValue(5000)]
    public int WebserverPort { get; set; } = 5000;

    [Category("Webserver")]
    [DisplayName("Bind-Adresse")]
    [Description("Netzwerkschnittstelle, auf der Kestrel lauscht. 'localhost' = nur lokal (Standard), " +
                 "'0.0.0.0' = von außen erreichbar. Bei 0.0.0.0 muss die API abgesichert werden!")]
    [DefaultValue("localhost")]
    public string WebserverHost { get; set; } = "localhost";

    [Category("Webserver")]
    [DisplayName("Database Debug")]
    [Description("Wenn aktiv, werden detaillierte Verbindungsinformationen im Terminal angezeigt.")]
    [DefaultValue(false)]
    public bool DbConnectionDebug { get; set; } = false;

    [Category("Webserver")]
    [DisplayName("API-Token (Steuerbefehle)")]
    [Description("Schützt die schreibenden Endpunkte (Jobs, Trader-Steuerung, Dev). Aufrufer müssen dann " +
                 "den Header X-Predictalytics-Key mitschicken. Bei einer Bind-Adresse außerhalb von localhost " +
                 "ist der Token Pflicht — ohne ihn werden die Steuerbefehle gar nicht erst bereitgestellt.")]
    [PasswordPropertyText(true)]
    public string ApiControlToken { get; set; } = "";

    /// <summary>
    /// Ob die Bind-Adresse den Dienst ueber das Netz erreichbar macht. Bei localhost
    /// uebernimmt das Betriebssystem die Absicherung, sonst nicht mehr.
    /// </summary>
    [Browsable(false)]
    [JsonIgnore]
    public bool IsPubliclyBound
    {
        get
        {
            var host = (WebserverHost ?? "").Trim();
            return host.Length > 0
                && !host.Equals("localhost", StringComparison.OrdinalIgnoreCase)
                && host != "127.0.0.1"
                && host != "::1";
        }
    }

    // ─── Egress ───

    private string _egressChannelsText = "";

    [Category("Egress (Proxy/IP)")]
    [DisplayName("Egress Channels")]
    [Description("Liste der Egress-Kanäle im Format: id|type|value (Zeilengetrennt). " +
                 "Beispiel: prox-1|Proxy|http://user:pass@proxy:8080\nip-1|SourceIp|192.168.1.100")]
    public string EgressChannelsText { get => _egressChannelsText; set => _egressChannelsText = value ?? ""; }

    // ─── Deployment Center ───

    [Category("Deployment Center")]
    [DisplayName("Server URL")]
    [Description("Basis-URL des Deployment Centers. Fest einkompiliert — ein einstellbarer Endpoint würde erlauben, die App auf einen gefälschten Lizenz- oder Update-Server zu zeigen.")]
    [ReadOnly(true)]
    [JsonIgnore]
    public string DcServerUrl => DcConfig.BaseUrl;

    [Category("Deployment Center")]
    [DisplayName("API Token")]
    [Description("Token des Deployment Centers (Authorization: Bearer). Benötigte Rechte: 'watchdog:ping' für Heartbeats, 'bugtracker:report' für das Fehler-Reporting. Ohne Token werden weder Heartbeats noch Fehler gemeldet.")]
    [PasswordPropertyText(true)]
    public string DcToken { get; set; } = "";

    [Category("Deployment Center")]
    [DisplayName("Heartbeat aktiv")]
    [Description("Sendet periodische Heartbeats an den Watchdog des Deployment Centers (Dead-Man's-Switch). Benötigt ein Token.")]
    [DefaultValue(true)]
    public bool DcHeartbeatEnabled { get; set; } = true;

    [Category("Deployment Center")]
    [DisplayName("Monitor Source")]
    [Description("Eindeutiger Monitor-Name dieses Dienstes im Watchdog-Dashboard.")]
    [DefaultValue("Predictalytics")]
    public string DcSource { get; set; } = "Predictalytics";

    [Category("Deployment Center")]
    [DisplayName("Monitor Instance")]
    [Description("Instanz-Kennung, falls mehrere Predictalytics-Instanzen laufen.")]
    [DefaultValue("default")]
    public string DcInstance { get; set; } = "default";

    [Category("Deployment Center")]
    [DisplayName("Heartbeat-Intervall (Sekunden)")]
    [Description("Sende-Takt der Heartbeats. Der Evaluator stuft nach dem Doppelten auf 'warning' und nach dem Vierfachen auf 'down'.")]
    [DefaultValue(60)]
    public int DcHeartbeatIntervalSeconds { get; set; } = 60;

    [Category("Deployment Center")]
    [DisplayName("Fehler melden")]
    [Description("Meldet Laufzeitfehler (Error/Fatal) an den Fehler-Stream des Deployment Centers. Gleiche Fehler werden dort gruppiert und hochgezählt.")]
    [DefaultValue(true)]
    public bool DcErrorReportingEnabled { get; set; } = true;

    [Category("Deployment Center")]
    [DisplayName("Update-Prüfung beim Start")]
    [Description("Prüft beim Start, ob im gewählten Kanal ein neueres Release vorliegt. Installiert wird nichts automatisch.")]
    [DefaultValue(true)]
    public bool DcUpdateCheckEnabled { get; set; } = true;

    [Category("Deployment Center")]
    [DisplayName("Update-Kanal")]
    [Description("prod, beta oder dev.")]
    [DefaultValue("prod")]
    public string DcUpdateChannel { get; set; } = "prod";

    // ─── Database ───

    private string _dbServer = "localhost";
    private string _dbName = "";
    private string _dbUser = "";
    private string _dbPassword = "";

    [Category("Database")]
    [DisplayName("Server")]
    public string DbServer { get => _dbServer; set => _dbServer = string.IsNullOrWhiteSpace(value) ? _dbServer : value.Trim(); }

    [Category("Database")]
    [DisplayName("Database")]
    public string DbName { get => _dbName; set => _dbName = string.IsNullOrWhiteSpace(value) ? _dbName : value.Trim(); }

    [Category("Database")]
    [DisplayName("User")]
    public string DbUser { get => _dbUser; set => _dbUser = string.IsNullOrWhiteSpace(value) ? _dbUser : value.Trim(); }

    [Category("Database")]
    [DisplayName("Password")]
    [PasswordPropertyText(true)]
    public string DbPassword { get => _dbPassword; set => _dbPassword = string.IsNullOrWhiteSpace(value) ? _dbPassword : value.Trim(); }

    [Category("Database")]
    [DisplayName("SSL Mode")]
    [Description("Verschlüsselung der MySQL-Verbindung. 'None' = unverschlüsselt (bisheriges Verhalten), " +
                 "'Preferred' = verschlüsselt wenn der Server es unterstützt. Bei entfernten Servern 'Preferred' empfohlen.")]
    [DefaultValue(MySqlConnector.MySqlSslMode.None)]
    public MySqlConnector.MySqlSslMode DbSslMode { get; set; } = MySqlConnector.MySqlSslMode.None;

    [Browsable(false)]
    [JsonIgnore]
    public string ConnectionString
    {
        get
        {
            var builder = new MySqlConnector.MySqlConnectionStringBuilder
            {
                Server = DbServer?.Trim(),
                Database = DbName?.Trim(),
                UserID = DbUser?.Trim(),
                Password = DbPassword?.Trim(),
                AllowPublicKeyRetrieval = true,
                SslMode = DbSslMode,
                Pooling = true,
                MinimumPoolSize = 0,
                MaximumPoolSize = 100
            };
            return builder.ConnectionString;
        }
    }

    /// <summary>Kestrel-Bind-URL aus Host und Port.</summary>
    [Browsable(false)]
    [JsonIgnore]
    public string WebserverUrl =>
        $"http://{(string.IsNullOrWhiteSpace(WebserverHost) ? "localhost" : WebserverHost.Trim())}:{WebserverPort}";

    // ─── Persistenz ───

    /// <summary>
    /// Ablageort der Einstellungen: %APPDATA%\Predictalytics unter Windows,
    /// ~/.config/Predictalytics unter Linux. Bewusst nicht neben der Programmdatei —
    /// deren Verzeichnis ist unter Linux ueblicherweise nicht beschreibbar.
    /// </summary>
    public static string SettingsDirectory => Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        "Predictalytics");

    public static string SettingsFilePath => Path.Combine(SettingsDirectory, FileName);

    /// <summary>Alter Ablageort neben der Programmdatei (bis einschliesslich der WinForms-Fassung).</summary>
    private static string LegacySettingsFilePath => Path.Combine(AppContext.BaseDirectory, FileName);

    public static PredictalyticsOptions Load()
    {
        // Neuer Ort hat Vorrang; sonst einmalig aus dem alten uebernehmen, damit
        // bestehende Zugangsdaten beim Umstieg nicht verloren gehen.
        var path = File.Exists(SettingsFilePath) ? SettingsFilePath
                 : File.Exists(LegacySettingsFilePath) ? LegacySettingsFilePath
                 : null;

        if (path == null)
        {
            var fresh = new PredictalyticsOptions();
            fresh.Save();
            return fresh;
        }

        try
        {
            var options = JsonSerializer.Deserialize<PredictalyticsOptions>(File.ReadAllText(path))
                          ?? new PredictalyticsOptions();
            if (path == LegacySettingsFilePath) options.Save();   // Migration festschreiben
            return options;
        }
        catch
        {
            return new PredictalyticsOptions();
        }
    }

    public void Save()
    {
        Directory.CreateDirectory(SettingsDirectory);
        File.WriteAllText(SettingsFilePath,
            JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true }));
    }
}
