using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Windows.Forms;
using PolyTrader.Core.Persistence;
using PolyTrader.Core.Security;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;

namespace PolyTraderSharp.Ui.Views
{
    /// <summary>
    /// Core-Settings-Fenster: allgemeine Server-Einstellungen (PropertyGrid) und die
    /// allgemeine Verwaltung der Polymarket-Accounts (Wallets, API-Keys …).
    /// Die copytrading-spezifischen Detail-Limits werden NICHT hier, sondern im
    /// Copytrading-Modul-View bearbeitet (AccountState ist bewusst general-only).
    /// </summary>
    public partial class SettingsView : Form
    {
        private const string SettingsPath = "server_settings.xml";

        private ServerSettings _settings = new();
        private MullvadVpnService? _vpn;
        private WatchdogHeartbeatService? _watchdog;
        private TerminalLogger? _logger;

        private IAccountRepository? _accountRepo;
        private TradingState? _state;
        private BindingList<AccountState> _accounts = new();

        public SettingsView()
        {
            InitializeComponent();

            btn_save.Click += (_, _) => Save();
            btn_loadsettings.Click += (_, _) => Reload();
            btnGenMasterKey.Click += (_, _) => GenerateMasterKey();
            btnSetOpenRouterKey.Click += (_, _) => SetOpenRouterKey();
            btnSetWatchdogToken.Click += (_, _) => SetWatchdogToken();
            btnTestWatchdog.Click += async (_, _) => await TestWatchdogAsync();
            btnSetLicenseKey.Click += (_, _) => SetLicenseKey();
            UpdateMasterKeyButtonState();

            btnAccNew.Click += (_, _) => AddAccount();
            btnAccDelete.Click += (_, _) => DeleteAccount();
            dgvAccounts.SelectionChanged += (_, _) =>
            {
                pgAccount.SelectedObject = dgvAccounts.CurrentRow?.DataBoundItem as AccountState;
            };
            pgAccount.PropertyValueChanged += (_, _) =>
            {
                if (pgAccount.SelectedObject is AccountState acc) SaveAccount(acc);
            };
        }

        public void Initialize(MullvadVpnService vpn, TerminalLogger logger,
            IAccountRepository accountRepo, TradingState state, WatchdogHeartbeatService? watchdog = null)
        {
            _vpn = vpn;
            _logger = logger;
            _accountRepo = accountRepo;
            _state = state;
            _watchdog = watchdog;

            Reload();
            LoadAccounts();
        }

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

        private void Reload()
        {
            _settings = ServerSettings.Load(SettingsPath);
            propertyGrid.SelectedObject = _settings;
        }

        private void Save()
        {
            try
            {
                _settings.Save(SettingsPath);
                _vpn?.ReloadSettings();
                _watchdog?.ReloadSettings();
                _logger?.Info("Server-Einstellungen gespeichert und Services neu geladen.");
                MessageBox.Show("Server-Einstellungen gespeichert.", "Erfolg",
                    MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Fehler beim Speichern: {ex.Message}", "Fehler",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // ===== Polymarket Accounts (allgemeine Einstellungen) =====

        private void LoadAccounts()
        {
            if (_state == null) return;
            _accounts = new BindingList<AccountState>(_state.Accounts.Values.OrderBy(a => a.AccountId).ToList());
            dgvAccounts.DataSource = _accounts;
            pgAccount.SelectedObject = dgvAccounts.CurrentRow?.DataBoundItem as AccountState;
        }

        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);
            dgvAccounts.CurrentCell = dgvAccounts.Rows[dgvAccounts.Rows.Count - 1].Cells[0];
        }

        private void DeleteAccount()
        {
            if (_state == null || _accountRepo == null) return;
            if (dgvAccounts.CurrentRow?.DataBoundItem is not AccountState acc) return;

            if (MessageBox.Show($"Account '{acc.Name}' (ID {acc.AccountId}) wirklich löschen?",
                    "Löschen bestätigen", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes)
                return;

            _state.Accounts.TryRemove(acc.AccountId, out _);
            _accountRepo.Delete(acc.AccountId);
            _accounts.Remove(acc);
        }

        private void SaveAccount(AccountState acc)
        {
            _accountRepo?.Upsert(acc);
            _state?.Accounts.AddOrUpdate(acc.AccountId, acc, (_, _) => acc);
            dgvAccounts.Refresh();
        }

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

        /// <summary>
        /// Setzt/entfernt den Watchdog-Agent-Token über eine maskierte Eingabe – damit das Secret
        /// nicht offen im PropertyGrid steht und die server_settings.xml nicht per Hand bearbeitet
        /// werden muss. Ist ein Master-Key gesetzt, wird der Token verschlüsselt abgelegt.
        /// Wird sofort gespeichert, damit der Test-Button und der laufende Dienst ihn direkt nutzen.
        /// </summary>
        private void SetWatchdogToken()
        {
            bool exists = !string.IsNullOrWhiteSpace(_settings.WatchdogToken);
            string? token = PromptForSecret("Watchdog Agent-Token",
                "Agent-Token aus dem Watchdog-Admin eingeben (Header X-Watchdog-Key).\n" +
                "Wird in der gitignorierten server_settings.xml gespeichert (leer = entfernen).\n" +
                (exists ? $"Aktuell: {_settings.WatchdogTokenStatus}." : "Aktuell ist KEIN Token gesetzt."));
            if (token == null) return;   // Abbruch

            try
            {
                if (string.IsNullOrWhiteSpace(token))
                {
                    _settings.WatchdogToken = "";
                    _settings.Save(SettingsPath);
                    _watchdog?.ReloadSettings();
                    propertyGrid.Refresh();
                    _logger?.Info("Watchdog-Agent-Token entfernt.");
                    MessageBox.Show("Watchdog-Token entfernt.", "Watchdog",
                        MessageBoxButtons.OK, MessageBoxIcon.Information);
                    return;
                }

                // Protect() gibt ohne Master-Key den Klartext unverändert zurück – kein stiller
                // Sicherheitsverlust, der Nutzer wird darauf hingewiesen.
                _settings.WatchdogToken = SecretProtection.Protect(token.Trim());
                _settings.Save(SettingsPath);
                _watchdog?.ReloadSettings();
                propertyGrid.Refresh();

                bool encrypted = SecretProtection.IsEncrypted(_settings.WatchdogToken);
                _logger?.Info($"Watchdog-Agent-Token gespeichert ({(encrypted ? "verschlüsselt" : "Klartext")}).");
                MessageBox.Show(
                    "Watchdog-Token gespeichert und sofort aktiv.\n\n" +
                    (encrypted
                        ? "Der Token liegt mit dem Master-Key verschlüsselt in server_settings.xml."
                        : "Hinweis: Es ist kein Master-Key gesetzt – der Token liegt im Klartext in der " +
                          "(gitignorierten) server_settings.xml. Mit „Master-Key erzeugen\" kannst du das ändern."),
                    "Watchdog", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Fehler beim Speichern des Watchdog-Tokens: {ex.Message}",
                    "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Sendet einen Heartbeat mit den GESPEICHERTEN Einstellungen und meldet das Ergebnis.
        /// Ungespeicherte Änderungen im PropertyGrid wirken bewusst nicht – sonst würde ein
        /// erfolgreicher Test eine Konfiguration bestätigen, die so nicht auf der Platte liegt.
        /// </summary>
        private async Task TestWatchdogAsync()
        {
            if (_watchdog == null)
            {
                MessageBox.Show("Der Watchdog-Dienst ist nicht verfügbar.", "Watchdog",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            btnTestWatchdog.Enabled = false;
            try
            {
                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}).");
                    MessageBox.Show(
                        $"Test-Heartbeat erfolgreich gesendet ({result.Detail}).\n\n" +
                        "Der Monitor sollte im Watchdog-Dashboard jetzt auf 'up' stehen.",
                        "Watchdog", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    _logger?.Warning($"⚠️ Watchdog-Test-Heartbeat fehlgeschlagen: {result.Detail}");
                    MessageBox.Show(
                        $"Test-Heartbeat fehlgeschlagen:\n\n{result.Detail}\n\n" +
                        "Bitte URL, Agent-Token und Source prüfen und die Einstellungen vorher speichern.",
                        "Watchdog", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            finally
            {
                btnTestWatchdog.Enabled = true;
            }
        }

        // ===== Lizenz (LicenseLabrador) =====

        /// <summary>
        /// Öffnet denselben Lizenzdialog wie der Programmstart (Hardware-ID + Server-Validierung),
        /// nur im Verwalten-Modus. Ein erfolgreich validierter Schlüssel wird – bei gesetztem
        /// Master-Key verschlüsselt – in server_settings.xml gespeichert und gilt ab dem nächsten Start.
        /// </summary>
        private void SetLicenseKey()
        {
            string current = string.Empty;
            try { current = SecretProtection.Unprotect(_settings.LicenseKey); }
            catch { /* nicht lesbar → als leer behandeln */ }

            LicenseLabrador.Client.LicenseClient client;
            try
            {
                client = new LicenseLabrador.Client.LicenseClient(PolyTraderSharp.Licensing.LicenseGate.BuildConfig());
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Lizenzprüfung nicht verfügbar: {ex.Message}", "Lizenz",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }

            using var dlg = new LicenseDialog(client, current, initialResult: null, startupContext: false);
            dlg.ShowDialog(this);

            if (dlg.ValidatedResult == null || !dlg.ValidatedResult.IsUsable || string.IsNullOrWhiteSpace(dlg.ValidatedKey))
                return;   // abgebrochen oder nicht validiert – nichts speichern

            try
            {
                _settings.LicenseKey = SecretProtection.Protect(dlg.ValidatedKey!.Trim());
                _settings.Save(SettingsPath);
                propertyGrid.Refresh();

                bool encrypted = SecretProtection.IsEncrypted(_settings.LicenseKey);
                _logger?.Info($"Lizenz validiert und gespeichert ({(encrypted ? "verschlüsselt" : "Klartext")}).");
                MessageBox.Show(
                    "Lizenz validiert und gespeichert.\n\n" +
                    (encrypted
                        ? "Der Schlüssel liegt mit dem Master-Key verschlüsselt in server_settings.xml."
                        : "Hinweis: Es ist kein Master-Key gesetzt – der Schlüssel liegt im Klartext in der " +
                          "(gitignorierten) server_settings.xml."),
                    "Lizenz", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Fehler beim Speichern des Lizenzschlüssels: {ex.Message}",
                    "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

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

        /// <summary>Pfad der Master-Key-Datei – identisch zu Program.cs (App-Ordner, gitignored).</summary>
        private static string MasterKeyFilePath => Path.Combine(AppContext.BaseDirectory, "master.key");

        /// <summary>Existiert bereits ein Master-Key (Umgebungsvariable ODER Datei)?</summary>
        private static bool MasterKeyExists() =>
            !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("POLYTRADER_MASTER_KEY"))
            || File.Exists(MasterKeyFilePath);

        /// <summary>Button nur aktiv, solange KEIN Master-Key existiert (Überschreiben = Lockout-Gefahr).</summary>
        private void UpdateMasterKeyButtonState() => btnGenMasterKey.Enabled = !MasterKeyExists();

        private void GenerateMasterKey()
        {
            // Sicherheitsnetz gegen Race/Doppelklick: einen bestehenden Key NIEMALS überschreiben.
            if (MasterKeyExists())
            {
                MessageBox.Show(
                    "Es existiert bereits ein Master-Key – Erzeugung abgebrochen. Ein Überschreiben würde den " +
                    "Zugriff auf bereits verschlüsselte Wallet-Keys unwiederbringlich zerstören.",
                    "Master-Key vorhanden", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                UpdateMasterKeyButtonState();
                return;
            }

            try
            {
                byte[] keyBytes = RandomNumberGenerator.GetBytes(32);          // 256-Bit-Schlüssel
                File.WriteAllText(MasterKeyFilePath, Convert.ToBase64String(keyBytes));
                _logger?.Info("🔐 Master-Key erzeugt und in master.key gespeichert. At-rest-Verschlüsselung wird beim nächsten Start aktiv.");

                MessageBox.Show(
                    "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 und sicher (z. B. 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.",
                    "Master-Key erzeugt", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Fehler beim Erzeugen des Master-Keys: {ex.Message}",
                    "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            UpdateMasterKeyButtonState();   // nach Erzeugung deaktivieren
        }

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

        /// <summary>
        /// Setzt/entfernt den OpenRouter-API-Key des Supervisors. Ablage in der gitignorierten Datei
        /// openrouter.key (genau das liest der OpenRouterClient) – kein Secret im Klartext in
        /// server_settings.xml. Der Nutzer gibt den Key selbst maskiert ein.
        /// </summary>
        private void SetOpenRouterKey()
        {
            string keyFile = Path.Combine(AppContext.BaseDirectory, "openrouter.key");
            bool exists = File.Exists(keyFile) ||
                          !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("POLYTRADER_OPENROUTER_KEY"));
            string? key = PromptForSecret("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.");
                    MessageBox.Show("OpenRouter-Key entfernt.", "OpenRouter",
                        MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                else
                {
                    File.WriteAllText(keyFile, key.Trim());
                    _logger?.Info("OpenRouter-Key gespeichert (openrouter.key).");
                    MessageBox.Show("OpenRouter-Key gespeichert. Wirkt beim nächsten Programmstart bzw. der nächsten Supervisor-Anfrage.\n\n" +
                                    "Tipp: bei OpenRouter ein Spend-Limit für diesen Key setzen (separater Key für den Supervisor empfohlen).",
                        "OpenRouter", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Fehler beim Speichern des OpenRouter-Keys: {ex.Message}",
                    "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>Modaler Mini-Dialog mit maskierter Eingabe. Rückgabe null = Abbruch.</summary>
        private string? PromptForSecret(string title, string prompt)
        {
            using var form = new Form
            {
                Text = title,
                Width = 480,
                Height = 210,
                FormBorderStyle = FormBorderStyle.FixedDialog,
                StartPosition = FormStartPosition.CenterParent,
                MinimizeBox = false,
                MaximizeBox = false
            };
            var lbl = new Label { Text = prompt, Left = 12, Top = 12, Width = 445, Height = 70, AutoSize = false };
            var tb = new TextBox { Left = 12, Top = 92, Width = 445, UseSystemPasswordChar = true };
            var ok = new Button { Text = "OK", DialogResult = DialogResult.OK, Left = 296, Top = 128, Width = 75 };
            var cancel = new Button { Text = "Abbrechen", DialogResult = DialogResult.Cancel, Left = 380, Top = 128, Width = 77 };
            form.Controls.AddRange(new Control[] { lbl, tb, ok, cancel });
            form.AcceptButton = ok;
            form.CancelButton = cancel;
            return form.ShowDialog(this) == DialogResult.OK ? tb.Text : null;
        }
    }
}
