using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Deploymentcenter.Client;
using PolyTrader.App.Avalonia.Controls;
using PolyTrader.Core;
using PolyTrader.Core.Configuration;
using PolyTrader.Core.Modularity;
using PolyTrader.Core.Persistence;
using PolyTrader.Core.Security;
using PolyTraderSharp;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;

namespace PolyTrader.App.Avalonia.Views
{
    /// <summary>
    /// Einstellungen: allgemeine Server-Settings und die Polymarket-Accounts. Beide Bereiche nutzen
    /// den <see cref="SettingsEditor"/> anstelle der früheren PropertyGrids.
    /// Layout vollständig in SettingsWindow.axaml.
    /// </summary>
    public partial class SettingsWindow : UserControl
    {
        private const string SettingsPath = "server_settings.xml";

        private readonly ObservableCollection<AccountState> _accounts = new();

        private ServerSettings _settings = new();
        private IAccountRepository? _accountRepo;
        private TradingState? _state;
        private WatchdogHeartbeatService? _watchdog;
        private MullvadVpnService? _vpn;
        private TerminalLogger? _logger;
        private DeploymentcenterOptions? _dcOptions;

        public SettingsWindow() => AvaloniaXamlLoader.Load(this);

        public SettingsWindow(IModuleUiHost host, IAccountRepository accountRepo, TradingState state,
            TerminalLogger logger, WatchdogHeartbeatService? watchdog = null,
            MullvadVpnService? vpn = null, DeploymentcenterOptions? dcOptions = null) : this()
        {
            _accountRepo = accountRepo;
            _state = state;
            _logger = logger;
            _watchdog = watchdog;
            _vpn = vpn;
            _dcOptions = dcOptions;

            var grid = this.FindControl<DataGrid>("gridAccounts")!;
            grid.ItemsSource = _accounts;
            grid.SelectionChanged += (_, _) =>
                this.FindControl<SettingsEditor>("editorAccount")!.Show(grid.SelectedItem);

            this.FindControl<Button>("btnSave")!.Click += (_, _) => SaveServerSettings();
            this.FindControl<Button>("btnReload")!.Click += (_, _) => ReloadServerSettings();
            this.FindControl<Button>("btnGenMasterKey")!.Click += async (_, _) => await GenerateMasterKeyAsync();
            this.FindControl<Button>("btnOpenRouterKey")!.Click += async (_, _) => await SetOpenRouterKeyAsync();
            this.FindControl<Button>("btnWatchdogToken")!.Click += async (_, _) => await SetWatchdogTokenAsync();
            this.FindControl<Button>("btnTestWatchdog")!.Click += async (_, _) => await TestWatchdogAsync();
            this.FindControl<Button>("btnLicenseKey")!.Click += async (_, _) => await SetLicenseKeyAsync();
            this.FindControl<Button>("btnCheckLicense")!.Click += async (_, _) => await CheckLicenseAsync();

            this.FindControl<Button>("btnAccNew")!.Click += (_, _) => AddAccount();
            this.FindControl<Button>("btnAccDelete")!.Click += async (_, _) => await DeleteAccountAsync();
            this.FindControl<Button>("btnAccSave")!.Click += (_, _) => SaveSelectedAccount();

            ReloadServerSettings();
            LoadAccounts();
            UpdateMasterKeyButtonState();
        }

        private void Status(string text) => this.FindControl<TextBlock>("lblStatus")!.Text = text;

        // ===== Server-Settings =====

        private void ReloadServerSettings()
        {
            _settings = ServerSettings.Load(SettingsPath);
            this.FindControl<SettingsEditor>("editorServer")!.Show(_settings);
            Status("Einstellungen geladen.");
        }

        private void SaveServerSettings()
        {
            try
            {
                _settings.Save(SettingsPath);
                _vpn?.ReloadSettings();
                _watchdog?.ReloadSettings();
                _logger?.Info("Server-Einstellungen gespeichert und Dienste neu geladen.");
                Status($"Gespeichert um {DateTime.Now:HH:mm:ss}.");
            }
            catch (Exception ex)
            {
                Status($"Speichern fehlgeschlagen: {ex.Message}");
            }
        }

        // ===== Polymarket Accounts =====

        private void LoadAccounts()
        {
            if (_state == null) return;
            _accounts.Clear();
            foreach (var a in _state.Accounts.Values.OrderBy(a => a.AccountId)) _accounts.Add(a);

            var grid = this.FindControl<DataGrid>("gridAccounts")!;
            if (_accounts.Count > 0) grid.SelectedIndex = 0;
        }

        private void AddAccount()
        {
            if (_state == null || _accountRepo == null) return;

            int newId = _state.Accounts.Count > 0 ? _state.Accounts.Keys.Max() + 1 : 1;
            var acc = new AccountState { AccountId = newId, Name = "Neuer Account" };

            _state.Accounts[acc.AccountId] = acc;
            _accountRepo.Upsert(acc);
            _accounts.Add(acc);

            this.FindControl<DataGrid>("gridAccounts")!.SelectedItem = acc;
            Status($"Account #{newId} angelegt.");
        }

        private async System.Threading.Tasks.Task DeleteAccountAsync()
        {
            if (_state == null || _accountRepo == null) return;
            if (this.FindControl<DataGrid>("gridAccounts")!.SelectedItem is not AccountState acc) return;

            bool ok = await DialogWindow.Confirm(this, "Löschen bestätigen",
                $"Account „{acc.Name}\" (ID {acc.AccountId}) wirklich löschen?", "Löschen");
            if (!ok) return;

            _state.Accounts.TryRemove(acc.AccountId, out _);
            _accountRepo.Delete(acc.AccountId);
            _accounts.Remove(acc);
            this.FindControl<SettingsEditor>("editorAccount")!.Show(null);
            Status($"Account #{acc.AccountId} gelöscht.");
        }

        private void SaveSelectedAccount()
        {
            if (this.FindControl<DataGrid>("gridAccounts")!.SelectedItem is not AccountState acc) return;
            _accountRepo?.Upsert(acc);
            _state?.Accounts.AddOrUpdate(acc.AccountId, acc, (_, _) => acc);
            Status($"Account #{acc.AccountId} gespeichert um {DateTime.Now:HH:mm:ss}.");
        }

        // ===== Master-Key (at-rest-Verschlüsselung) =====

        private static string MasterKeyFilePath => Path.Combine(AppContext.BaseDirectory, "master.key");

        private static bool MasterKeyExists() =>
            !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("POLYTRADER_MASTER_KEY"))
            || File.Exists(MasterKeyFilePath);

        /// <summary>Schaltfläche nur aktiv, solange KEIN Master-Key existiert (Überschreiben = Lockout-Gefahr).</summary>
        private void UpdateMasterKeyButtonState() =>
            this.FindControl<Button>("btnGenMasterKey")!.IsEnabled = !MasterKeyExists();

        private async System.Threading.Tasks.Task GenerateMasterKeyAsync()
        {
            // Sicherheitsnetz gegen Race/Doppelklick: einen bestehenden Key NIEMALS überschreiben.
            if (MasterKeyExists())
            {
                await DialogWindow.Info(this, "Master-Key vorhanden",
                    "Es existiert bereits ein Master-Key – Erzeugung abgebrochen. Ein Überschreiben würde den " +
                    "Zugriff auf bereits verschlüsselte Wallet-Keys unwiederbringlich zerstören.");
                UpdateMasterKeyButtonState();
                return;
            }

            try
            {
                byte[] keyBytes = RandomNumberGenerator.GetBytes(32);   // 256-Bit-Schlüssel
                File.WriteAllText(MasterKeyFilePath, Convert.ToBase64String(keyBytes));
                RestrictToOwner(MasterKeyFilePath);

                _logger?.Info("🔐 Master-Key erzeugt und in master.key gespeichert. At-rest-Verschlüsselung wird beim nächsten Start aktiv.");
                await DialogWindow.Info(this, "Master-Key erzeugt",
                    "Ein zufälliger 32-Byte-Master-Key wurde erzeugt und in der Datei „master.key\" " +
                    "(App-Ordner, gitignored) gespeichert.\n\n" +
                    "WICHTIG:\n" +
                    "• Sichere diese Datei SOFORT separat (Passwort-Manager / Offline-Backup).\n" +
                    "• Master-Key-Verlust = KEIN Zugriff mehr auf die verschlüsselten Wallet-Keys!\n" +
                    "• Die Verschlüsselung der Account-Credentials wird beim nächsten Programmstart aktiv.");
            }
            catch (Exception ex)
            {
                await DialogWindow.Info(this, "Fehler", $"Fehler beim Erzeugen des Master-Keys: {ex.Message}");
            }

            UpdateMasterKeyButtonState();
        }

        /// <summary>
        /// Beschränkt eine Schlüsseldatei auf den Besitzer. Auf Linux legt <see cref="File.WriteAllText"/>
        /// mit der üblichen umask sonst <c>644</c> an – die Datei wäre für alle lesbar. Unter Windows
        /// greift die NTFS-Vererbung, dort ist nichts zu tun.
        /// </summary>
        private void RestrictToOwner(string path) =>
            FilePermissions.RestrictToOwner(path, msg => _logger?.Warning($"⚠️ {msg}"));

        // ===== OpenRouter-API-Key (Supervisor) =====

        private async System.Threading.Tasks.Task SetOpenRouterKeyAsync()
        {
            string keyFile = Path.Combine(AppContext.BaseDirectory, "openrouter.key");
            bool exists = File.Exists(keyFile) ||
                          !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("POLYTRADER_OPENROUTER_KEY"));

            string? key = await DialogWindow.Prompt(this, "OpenRouter-API-Key",
                "OpenRouter-API-Key für den Supervisor eingeben.\n" +
                "Wird in der gitignorierten Datei „openrouter.key\" gespeichert (leer = entfernen).\n" +
                (exists ? "Aktuell ist bereits ein Key hinterlegt." : "Aktuell ist KEIN Key gesetzt."));
            if (key == null) return;   // Abbruch

            try
            {
                if (string.IsNullOrWhiteSpace(key))
                {
                    if (File.Exists(keyFile)) File.Delete(keyFile);
                    _logger?.Info("OpenRouter-Key aus openrouter.key entfernt.");
                    Status("OpenRouter-Key entfernt.");
                    return;
                }

                File.WriteAllText(keyFile, key.Trim());
                RestrictToOwner(keyFile);
                _logger?.Info("OpenRouter-Key gespeichert (openrouter.key).");
                Status("OpenRouter-Key gespeichert – wirkt bei der nächsten Supervisor-Anfrage.");
            }
            catch (Exception ex)
            {
                await DialogWindow.Info(this, "Fehler", $"Fehler beim Speichern des OpenRouter-Keys: {ex.Message}");
            }
        }

        // ===== Watchdog =====

        private async System.Threading.Tasks.Task SetWatchdogTokenAsync()
        {
            bool exists = !string.IsNullOrWhiteSpace(_settings.WatchdogToken);
            string? token = await DialogWindow.Prompt(this, "Watchdog Agent-Token",
                "Agent-Token aus dem Watchdog-Admin eingeben.\n" +
                "Wird in der gitignorierten server_settings.xml gespeichert (leer = entfernen).\n" +
                (exists ? "Aktuell ist ein Token gesetzt." : "Aktuell ist KEIN Token gesetzt."));
            if (token == null) return;   // Abbruch

            try
            {
                // Protect() gibt ohne Master-Key den Klartext unverändert zurück – kein stiller
                // Sicherheitsverlust, es wird unten darauf hingewiesen.
                _settings.WatchdogToken = string.IsNullOrWhiteSpace(token)
                    ? string.Empty
                    : SecretProtection.Protect(token.Trim());
                _settings.Save(SettingsPath);
                _watchdog?.ReloadSettings();
                this.FindControl<SettingsEditor>("editorServer")!.Show(_settings);

                if (_settings.WatchdogToken.Length == 0)
                {
                    _logger?.Info("Watchdog-Agent-Token entfernt.");
                    Status("Watchdog-Token entfernt.");
                    return;
                }

                bool encrypted = SecretProtection.IsEncrypted(_settings.WatchdogToken);
                _logger?.Info($"Watchdog-Agent-Token gespeichert ({(encrypted ? "verschlüsselt" : "Klartext")}).");
                Status(encrypted
                    ? "Watchdog-Token gespeichert (verschlüsselt) und sofort aktiv."
                    : "Watchdog-Token gespeichert – ACHTUNG: ohne Master-Key im Klartext. Mit „Master-Key erzeugen\" ändern.");
            }
            catch (Exception ex)
            {
                await DialogWindow.Info(this, "Fehler", $"Fehler beim Speichern des Watchdog-Tokens: {ex.Message}");
            }
        }

        /// <summary>
        /// Sendet einen Heartbeat mit den GESPEICHERTEN Einstellungen. Ungespeicherte Änderungen
        /// wirken bewusst nicht – sonst würde ein erfolgreicher Test etwas bestätigen, das im
        /// laufenden Betrieb gar nicht gilt.
        /// </summary>
        private async System.Threading.Tasks.Task TestWatchdogAsync()
        {
            if (_watchdog == null) return;
            var btn = this.FindControl<Button>("btnTestWatchdog")!;
            btn.IsEnabled = false;
            try
            {
                Status("Sende Test-Heartbeat …");
                var result = await _watchdog.SendHeartbeatAsync("ok", "Test-Heartbeat aus dem PolyTrader-Settings-Fenster");

                if (result.Success)
                {
                    _logger?.Info($"✅ Watchdog-Test-Heartbeat erfolgreich gesendet ({result.Detail}).");
                    Status($"Test-Heartbeat erfolgreich ({result.Detail}). Der Monitor sollte jetzt auf „up\" stehen.");
                }
                else
                {
                    _logger?.Warning($"⚠️ Watchdog-Test-Heartbeat fehlgeschlagen: {result.Detail}");
                    await DialogWindow.Info(this, "Watchdog",
                        $"Test-Heartbeat fehlgeschlagen:\n\n{result.Detail}\n\n" +
                        "Bitte URL, Agent-Token und Source prüfen – und die Einstellungen vorher speichern.");
                    Status("Test-Heartbeat fehlgeschlagen.");
                }
            }
            finally
            {
                btn.IsEnabled = true;
            }
        }

        // ===== Lizenz (D-2) =====

        private async System.Threading.Tasks.Task SetLicenseKeyAsync()
        {
            bool exists = !string.IsNullOrWhiteSpace(_settings.LicenseKey);
            string? key = await DialogWindow.Prompt(this, "Deploymentcenter-Lizenzschlüssel",
                "Lizenzschlüssel eingeben.\n" +
                "Wird in der gitignorierten server_settings.xml gespeichert (leer = entfernen).\n" +
                "Wirkt erst beim nächsten Start von PolyTrader (keine Laufzeit-Revalidierung).\n" +
                (exists ? "Aktuell ist ein Schlüssel gesetzt." : "Aktuell ist KEIN Schlüssel gesetzt."));
            if (key == null) return;   // Abbruch

            try
            {
                _settings.LicenseKey = string.IsNullOrWhiteSpace(key)
                    ? string.Empty
                    : SecretProtection.Protect(key.Trim());
                _settings.Save(SettingsPath);
                this.FindControl<SettingsEditor>("editorServer")!.Show(_settings);

                if (_settings.LicenseKey.Length == 0)
                {
                    _logger?.Info("Lizenzschlüssel entfernt.");
                    Status("Lizenzschlüssel entfernt – PolyTrader startet beim nächsten Mal im eingeschränkten Modus.");
                    return;
                }

                bool encrypted = SecretProtection.IsEncrypted(_settings.LicenseKey);
                _logger?.Info($"Lizenzschlüssel gespeichert ({(encrypted ? "verschlüsselt" : "Klartext")}).");
                Status((encrypted
                    ? "Lizenzschlüssel gespeichert (verschlüsselt). "
                    : "Lizenzschlüssel gespeichert – ACHTUNG: ohne Master-Key im Klartext. ") +
                    "Mit „Lizenz jetzt prüfen\" testen, wirkt für Module erst beim nächsten Start.");
            }
            catch (Exception ex)
            {
                await DialogWindow.Info(this, "Fehler", $"Fehler beim Speichern des Lizenzschlüssels: {ex.Message}");
            }
        }

        /// <summary>
        /// Prüft den GESPEICHERTEN Schlüssel gegen das Deploymentcenter und zeigt Urteil und
        /// Hardware-ID (zum Kopieren, falls Richard sie für eine Aktivierung im Admin braucht).
        /// Ungespeicherte Änderungen wirken bewusst nicht, aus demselben Grund wie beim Watchdog-Test.
        /// </summary>
        private async System.Threading.Tasks.Task CheckLicenseAsync()
        {
            if (_dcOptions == null) return;
            var btn = this.FindControl<Button>("btnCheckLicense")!;
            btn.IsEnabled = false;
            try
            {
                if (string.IsNullOrWhiteSpace(_dcOptions.BaseUrl))
                {
                    await DialogWindow.Info(this, "Lizenz", "Keine Deploymentcenter-URL konfiguriert (appsettings.json).");
                    return;
                }

                string key;
                try { key = SecretProtection.Unprotect(_settings.LicenseKey); }
                catch (Exception ex)
                {
                    await DialogWindow.Info(this, "Lizenz", $"Gespeicherter Schlüssel nicht lesbar: {ex.Message}");
                    return;
                }

                var hw = HardwareId.GetHardwareId(_dcOptions.ProjectSlug);

                if (string.IsNullOrWhiteSpace(key))
                {
                    await DialogWindow.Info(this, "Lizenz",
                        $"Kein Lizenzschlüssel gesetzt.\n\nHardware-ID dieser Maschine:\n{hw.HardwareId}");
                    Status("Kein Lizenzschlüssel gesetzt.");
                    return;
                }

                Status("Prüfe Lizenz …");
                LicenseClient.DefaultAppVersion = BuildInfo.Version;
                var client = new LicenseClient();
                var result = await client.ValidateAsync(_dcOptions.ProjectSlug, key, _dcOptions.BaseUrl, BuildInfo.Version);

                string detail = $"Status: {result.Status}\n{result.Message}\n\nHardware-ID dieser Maschine:\n{hw.HardwareId}";

                if (result.IsValid)
                {
                    _logger?.Info($"🔑 Lizenz geprüft: gültig ({result.Status}).");
                    Status($"Lizenz gültig ({result.Status}).");
                    await DialogWindow.Info(this, "Lizenz gültig", detail);
                }
                else
                {
                    _logger?.Warning($"⚠️ Lizenz geprüft: nicht gültig ({result.Status}).");
                    Status($"Lizenz NICHT gültig ({result.Status}).");
                    await DialogWindow.Info(this, "Lizenz nicht gültig", detail);
                }
            }
            catch (Exception ex)
            {
                await DialogWindow.Info(this, "Fehler", $"Lizenzprüfung fehlgeschlagen: {ex.Message}");
            }
            finally
            {
                btn.IsEnabled = true;
            }
        }
    }
}
