using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Deploymentcenter.Client;
using Microsoft.Extensions.DependencyInjection;
using PolyTrader.App.Avalonia.Shell;
using PolyTrader.App.Avalonia.ViewModels;
using PolyTrader.Core;
using PolyTrader.Core.Analytics;
using PolyTrader.Core.Configuration;
using PolyTrader.Core.Modularity;
using PolyTrader.Core.Persistence;
using PolyTrader.Core.Security;
using PolyTrader.Modules.Supervisor.Persistence;
using PolyTraderSharp;
using PolyTraderSharp.Models;
using PolyTraderSharp.Services;

namespace PolyTrader.App.Avalonia.Views
{
    /// <summary>
    /// Startseite der Anwendung – Gegenstück zur bisherigen LauncherForm. Führt zu den übrigen
    /// Ansichten, zeigt Kernkennzahlen und beinhaltet die Live-Überblick-Widgets.
    /// Layout vollständig in LauncherWindow.axaml.
    ///
    /// <para>Seit der Umstellung auf die Einfenster-Shell ist dies ein <see cref="UserControl"/>
    /// und kein Fenster mehr; der Klassenname bleibt vorerst, um die parallele
    /// Deploymentcenter-Arbeit an dieser Datei nicht mit einer Umbenennung zu kollidieren.
    /// Die Statusleiste (Trading/Module/API) und das Beenden liegen jetzt in
    /// <see cref="ShellWindow"/>.</para>
    /// </summary>
    public partial class LauncherWindow : UserControl
    {
        private readonly IModuleUiHost _uiHost;
        private readonly IServiceProvider _services;
        private readonly TradingState _state;
        private readonly DispatcherTimer _statusTimer = new() { Interval = TimeSpan.FromSeconds(1) };
        private readonly ObservableCollection<ViewTile> _windowButtons = new();
        private readonly ObservableCollection<AccountOverviewRow> _accountRows = new();
        private readonly ObservableCollection<ModuleKpiTile> _kpiTiles = new();
        private readonly ObservableCollection<NotableTradeRow> _notableRows = new();
        private readonly ObservableCollection<LogAlertRow> _alertRows = new();

        private int _statusTicks;

        /// <summary>Ergebnis der letzten Update-Prüfung – für den Klick auf „Jetzt installieren …".</summary>
        private UpdateCheckResult? _updateCheck;

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

        public LauncherWindow(IModuleUiHost uiHost, IServiceProvider services) : this()
        {
            _uiHost = uiHost;
            _services = services;
            _state = services.GetRequiredService<TradingState>();

            this.FindControl<ItemsControl>("windowButtons")!.ItemsSource = _windowButtons;
            this.FindControl<DataGrid>("gridAccounts")!.ItemsSource = _accountRows;
            this.FindControl<ItemsControl>("kpiTiles")!.ItemsSource = _kpiTiles;
            this.FindControl<DataGrid>("gridNotable")!.ItemsSource = _notableRows;
            this.FindControl<DataGrid>("gridAlerts")!.ItemsSource = _alertRows;

            RebuildWindowButtons();
            _uiHost.OpenStateChanged += OnOpenStateChanged;

            this.FindControl<Button>("btnLiveTrading")!.Click += (_, _) =>
            {
                _state.LiveTradingMode = Next(_state.LiveTradingMode);
                UpdateTradingToggles();
            };
            this.FindControl<Button>("btnDemoTrading")!.Click += (_, _) =>
            {
                _state.DemoTradingMode = Next(_state.DemoTradingMode);
                UpdateTradingToggles();
            };

            this.FindControl<Button>("btnInstallUpdate")!.Click += async (_, _) => await InstallUpdateAsync();
            this.FindControl<Button>("btnDismissUpdate")!.Click += (_, _) =>
                this.FindControl<Border>("pnlUpdateBanner")!.IsVisible = false;

            _statusTimer.Tick += (_, _) => UpdateStatus();
            _statusTimer.Start();
            UpdateStatus();
            RefreshData();

            // D-4: einmalige, stille Prüfung beim Start - fire-and-forget, darf den Start nie
            // verzögern oder blockieren. Nur aktiv, wenn Deploymentcenter:UpdateCheckEnabled gesetzt ist.
            _ = CheckForUpdateAsync();

            // Die Umschalterflaechen werden im Code gesetzt und folgen dem Thema nicht von selbst.
            // Kein Abmelden: die Seite lebt in der Einfenster-Shell bis zum Programmende, ein
            // Gegenstueck zu Window.Closed gibt es nicht (Sicherheitsabfrage und Beenden liegen
            // jetzt im ShellWindow).
            ThemeManager.ThemeChanged += UpdateTradingToggles;
        }

        /// <summary>Dreistufiger Wechsel wie bisher: Inactive -> SellOnly -> Active -> Inactive.</summary>
        private static TradingMode Next(TradingMode mode) => mode switch
        {
            TradingMode.Inactive => TradingMode.SellOnly,
            TradingMode.SellOnly => TradingMode.Active,
            _ => TradingMode.Inactive
        };

        private void OnOpenStateChanged()
        {
            if (Dispatcher.UIThread.CheckAccess()) RebuildWindowButtons();
            else Dispatcher.UIThread.Post(RebuildWindowButtons);
        }

        /// <summary>
        /// Kacheln zu den übrigen Ansichten. Die Startseite selbst bleibt aussen vor – sie ist ja
        /// gerade zu sehen; ihre Kachel würde nur ins Leere führen und dauerhaft als „offen" gelten.
        /// </summary>
        private void RebuildWindowButtons()
        {
            _windowButtons.Clear();
            foreach (var view in _uiHost.Views
                         .Where(v => v.Id != AvaloniaUiHost.StartViewId)
                         .OrderBy(v => v.Order).ThenBy(v => v.Title))
            {
                string id = view.Id;
                bool open = _uiHost.IsOpen(id);
                _windowButtons.Add(new ViewTile
                {
                    Title = view.Title,
                    Icon = ViewIcons.Resolve(view.IconKey),
                    IsCurrent = open,
                    IsChecked = open,
                    Command = new RelayCommand(() => _uiHost.OpenView(id))
                });
            }
        }

        private void UpdateTradingToggles()
        {
            Apply(this.FindControl<Button>("btnLiveTrading")!, "LiveTrading", _state.LiveTradingMode);
            Apply(this.FindControl<Button>("btnDemoTrading")!, "DemoTrading", _state.DemoTradingMode);

            static void Apply(Button btn, string label, TradingMode mode)
            {
                (string text, string brushKey) = mode switch
                {
                    TradingMode.Active => ($"{label} (AKTIV)", "AppToggleActiveBrush"),
                    TradingMode.SellOnly => ($"{label} (SELL-ONLY)", "AppToggleSellOnlyBrush"),
                    _ => ($"{label} (DEAKTIVIERT)", "AppToggleInactiveBrush")
                };
                btn.Content = text;
                btn.Background = ThemeManager.Brush(brushKey);
            }
        }

        /// <summary>
        /// Hält die Umschalter und die Widgets frisch. Die frühere Statusleiste dieser Seite ist
        /// in die Shell gewandert und wird dort im eigenen Takt aktualisiert.
        /// </summary>
        private void UpdateStatus()
        {
            UpdateTradingToggles();

            // Account-Übersicht + Widgets alle 30 s aktualisieren
            if (_statusTicks++ % 30 == 0)
            {
                RefreshData();
            }
        }

        private void RefreshData()
        {
            LoadAccountOverview();
            UpdateModuleKpis();
            UpdateAlerts();
            UpdateNotable();
            UpdateSupervisor();
        }

        // ===== Update-Prüfung (D-4) =====

        /// <summary>
        /// Prüft beim Start still auf ein neues Release. Installiert NIE automatisch (Leitentscheidung
        /// 4 aus dem Umsetzungsplan) - ein `is_critical`-Release ändert nur den Hinweistext, nicht das
        /// Verhalten. Wirft nie: ein nicht erreichbares Deploymentcenter darf den Start nicht stören.
        /// </summary>
        private async Task CheckForUpdateAsync()
        {
            try
            {
                var dcOptions = _services.GetService<DeploymentcenterOptions>();
                if (dcOptions == null || !dcOptions.UpdateCheckEnabled || string.IsNullOrWhiteSpace(dcOptions.BaseUrl))
                    return;

                var settings = _services.GetService<ServerSettings>();
                string? licenseKey = null;
                try { licenseKey = SecretProtection.Unprotect(settings?.LicenseKey ?? string.Empty); }
                catch { /* nicht lesbarer Schlüssel -> Prüfung läuft ohne Zugangsdaten weiter */ }

                var updateClient = new UpdateClient();
                var result = await updateClient.CheckForUpdateAsync(
                    baseUrl: dcOptions.BaseUrl,
                    projectId: dcOptions.ProjectSlug,
                    currentVersion: BuildInfo.Version,
                    channel: dcOptions.Channel,
                    credentials: ReleaseCredentials.FromLicenseKey(licenseKey));

                if (result.Unauthorized)
                {
                    // Kein Netzwerkfehler: die Lizenz trägt nicht mehr für den Paketabruf. Kein
                    // Alarm im Launcher nötig - die Lizenzprüfung selbst (D-2) hat das schon gemeldet.
                    return;
                }

                if (!result.UpdateAvailable || result.LatestRelease == null) return;

                _updateCheck = result;
                string version = result.LatestRelease.Version;
                string critical = result.IsCritical ? " (empfohlenes Sicherheitsupdate)" : "";

                Dispatcher.UIThread.Post(() =>
                {
                    this.FindControl<TextBlock>("lblUpdateInfo")!.Text =
                        $"Update verfügbar: v{version}{critical}. {result.LatestRelease.Changelog}".TrimEnd();
                    this.FindControl<Border>("pnlUpdateBanner")!.IsVisible = true;
                });
            }
            catch
            {
                // Update-Prüfung darf den Start nie stören - stiller Rückzug, kein Log-Spam beim
                // vermutlich häufigsten Fall (Deploymentcenter nicht erreichbar).
            }
        }

        /// <summary>
        /// Startet den Update-Agent nach Bestätigung. Läuft über denselben geordneten Shutdown-Weg
        /// wie „Beenden" (inklusive der 10-Sekunden-Sicherheitsabfrage) - <c>exitCurrentApp: false</c>
        /// ist hier Pflicht, kein Environment.Exit bei offenen Positionen (Leitentscheidung 3).
        /// </summary>
        private async Task InstallUpdateAsync()
        {
            if (_updateCheck?.LatestRelease == null) return;

            var dcOptions = _services.GetService<DeploymentcenterOptions>();
            if (dcOptions == null) return;

            string? agentPath = UpdateClient.ResolveAgentPath();
            if (agentPath == null)
            {
                await DialogWindow.Info(this, "Update",
                    "Kein Update-Agent im Installationsverzeichnis gefunden. Diese Installation kann " +
                    "sich nicht selbst aktualisieren - bitte das neue Paket von Hand einspielen.");
                return;
            }

            bool confirmed = await DialogWindow.Confirm(this, "Update installieren",
                $"Version {_updateCheck.LatestRelease.Version} herunterladen und PolyTrader danach " +
                "neu starten?\n\nOffene Vorgänge werden dabei genauso geordnet beendet wie über „Beenden\".",
                "Installieren");
            if (!confirmed) return;

            var settings = _services.GetService<ServerSettings>();
            string? licenseKey = null;
            try { licenseKey = SecretProtection.Unprotect(settings?.LicenseKey ?? string.Empty); }
            catch { /* siehe CheckForUpdateAsync */ }

            UpdateClient.LaunchUpdateAgent(
                agentPath: agentPath,
                projectId: dcOptions.ProjectSlug,
                channel: dcOptions.Channel,
                action: "update",
                version: "latest",
                currentVersion: BuildInfo.Version,
                exitCurrentApp: false,
                waitForCurrentProcess: true,
                waitTimeoutSeconds: 40,
                licenseKey: licenseKey);

            // Derselbe Weg wie ein Klick auf "Beenden" - inklusive Sicherheitsabfrage. Der Agent
            // wartet ohnehin per --wait-for-pid auf unser Prozessende.
            _uiHost.RequestShutdown();
        }

        // ===== Account-Übersicht =====

        private void LoadAccountOverview()
        {
            var tradeLog = _services.GetService<ITradeLogRepository>();
            DateTime since3d = DateTime.UtcNow.AddDays(-3);

            _accountRows.Clear();
            if (_state?.Accounts == null) return;

            foreach (var acc in _state.Accounts.Values.OrderBy(a => a.AccountId))
            {
                List<TradeRecord> trades;
                try { trades = tradeLog?.Find(t => t.AccountId == acc.AccountId) ?? new List<TradeRecord>(); }
                catch { trades = new List<TradeRecord>(); }

                var (pnl3d, win3d, _) = TradeAnalytics.WindowSummary(trades.Where(t => t.ClosedAt >= since3d));
                string modules = trades
                    .Select(t => t.ModuleName)
                    .Where(m => !string.IsNullOrEmpty(m))
                    .Distinct().OrderBy(m => m)
                    .DefaultIfEmpty("—")
                    .Aggregate((a, b) => a + ", " + b);

                _accountRows.Add(new AccountOverviewRow
                {
                    AccountId = acc.AccountId,
                    Name = (string.IsNullOrEmpty(acc.Name) ? $"#{acc.AccountId}" : acc.Name) + (acc.IsDemo ? " (Demo)" : ""),
                    Modules = modules,
                    WalletAddress = acc.WalletAddress ?? string.Empty,
                    Balance = acc.TotalBalance,
                    Pnl3d = pnl3d,
                    WinRate3d = win3d,
                    OverallPnl = trades.Sum(t => t.RealizedPnl)
                });
            }
        }

        public async void OnOpenPolymarketProfileClick(object? sender, RoutedEventArgs e)
        {
            if ((sender as Button)?.DataContext is AccountOverviewRow row)
            {
                if (string.IsNullOrWhiteSpace(row.WalletAddress))
                {
                    await DialogWindow.Info(this, "Polymarket", "Für diesen Account ist keine Wallet-Adresse hinterlegt.");
                    return;
                }
                try
                {
                    var topLevel = TopLevel.GetTopLevel(this);
                    if (topLevel != null)
                    {
                        await topLevel.Launcher.LaunchUriAsync(new Uri($"https://polymarket.com/profile/{row.WalletAddress}"));
                    }
                }
                catch (Exception ex)
                {
                    await DialogWindow.Info(this, "Fehler", $"Konnte Polymarket nicht öffnen: {ex.Message}");
                }
            }
        }

        // ===== Modul-KPI-Kacheln =====

        private void UpdateModuleKpis()
        {
            _kpiTiles.Clear();
            var tradeLog = _services.GetService<ITradeLogRepository>();
            try
            {
                var since = DateTime.UtcNow.AddDays(-30);
                List<TradeRecord> all = tradeLog?.Find(t => t.ClosedAt >= since) ?? new List<TradeRecord>();

                DateTime today = DateTime.UtcNow.Date;
                DateTime week = DateTime.UtcNow.AddDays(-7);

                var modules = all.Select(t => t.ModuleName).Where(m => !string.IsNullOrEmpty(m)).Distinct().OrderBy(m => m).ToList();
                foreach (var module in modules)
                {
                    var moduleTrades = all.Where(t => t.ModuleName == module).ToList();
                    _kpiTiles.Add(BuildTile(module, moduleTrades, today, week));
                }

                _kpiTiles.Add(BuildTile("Gesamt", all, today, week));

                if (_kpiTiles.Count == 1 && all.Count == 0)
                {
                    _kpiTiles.Add(new ModuleKpiTile
                    {
                        ModuleName = "Hinweis",
                        Text = "Noch keine abgeschlossenen Trades.",
                        IsPositive = true
                    });
                }
            }
            catch (Exception ex)
            {
                _kpiTiles.Add(new ModuleKpiTile
                {
                    ModuleName = "Fehler",
                    Text = $"KPIs n/v: {ex.Message}",
                    IsPositive = false
                });
            }
        }

        private static ModuleKpiTile BuildTile(string module, List<TradeRecord> moduleTrades, DateTime today, DateTime week)
        {
            decimal todayPnl = moduleTrades.Where(t => t.ClosedAt >= today).Sum(t => t.RealizedPnl);
            var k7 = TradeAnalytics.ComputeKpis(moduleTrades.Where(t => t.ClosedAt >= week));
            var k30 = TradeAnalytics.ComputeKpis(moduleTrades);

            string text = $"{module}\n" +
                          $"Heute: {todayPnl:+0.00;-0.00} USDC\n" +
                          $"7T:  {k7.NetPnl:+0.00;-0.00} · {k7.WinRatePct:0}% · {k7.TradeCount} Tr.\n" +
                          $"30T: {k30.NetPnl:+0.00;-0.00} · {k30.WinRatePct:0}%";

            return new ModuleKpiTile
            {
                ModuleName = module,
                Text = text,
                IsPositive = k30.NetPnl >= 0
            };
        }

        // ===== Warnungen & Fehler =====

        private void UpdateAlerts()
        {
            var grpAlerts = this.FindControl<HeaderedContentControl>("grpAlerts");
            try
            {
                string logsDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
                string path = Path.Combine(logsDir, $"{DateTime.Now:yyyy-MM-dd}.jsonl");
                var rows = new List<LogAlertRow>();
                if (File.Exists(path))
                {
                    foreach (var line in File.ReadLines(path))
                    {
                        var p = LogJson.ParseLine(line);
                        if (p == null) continue;
                        if (p.Level != "Error" && p.Level != "Warning") continue;
                        rows.Add(new LogAlertRow
                        {
                            Time = ShortTime(p.Time),
                            Level = p.Level,
                            Message = OneLine(p.Message)
                        });
                    }
                }
                rows.Reverse(); // neueste zuerst
                _alertRows.Clear();
                foreach (var r in rows.Take(200)) _alertRows.Add(r);
                if (grpAlerts != null) grpAlerts.Header = $"Warnungen & Fehler (heute): {rows.Count}";
            }
            catch (Exception ex)
            {
                if (grpAlerts != null) grpAlerts.Header = $"Warnungen & Fehler – n/v ({ex.Message})";
            }
        }

        // ===== Auffällige Trades (24h) =====

        private void UpdateNotable()
        {
            try
            {
                var tradeLog = _services.GetService<ITradeLogRepository>();
                var since = DateTime.UtcNow.AddDays(-1);
                var rows = (tradeLog?.Find(t => t.ClosedAt >= since) ?? new List<TradeRecord>())
                    .OrderByDescending(t => Math.Abs(t.RealizedPnl))
                    .Take(30)
                    .Select(t => new NotableTradeRow
                    {
                        Module = t.ModuleName ?? string.Empty,
                        Market = t.MarketQuestion ?? string.Empty,
                        Pnl = t.RealizedPnl,
                        PnlPct = t.PnlPercent
                    })
                    .ToList();

                _notableRows.Clear();
                foreach (var r in rows) _notableRows.Add(r);
            }
            catch
            {
                _notableRows.Clear();
            }
        }

        // ===== Supervisor-KI-Kurzfassung =====

        private void UpdateSupervisor()
        {
            var txtSupervisor = this.FindControl<SelectableTextBlock>("txtSupervisor");
            if (txtSupervisor == null) return;

            var reports = _services.GetService<ISupervisorReportRepository>();
            if (reports == null)
            {
                txtSupervisor.Text = "Supervisor-Modul nicht verfügbar.";
                return;
            }
            try
            {
                var last = reports.GetRecent(1).FirstOrDefault();
                txtSupervisor.Text = last == null
                    ? "Noch kein Supervisor-Bericht. (OpenRouter-Key setzen und im Supervisor-Fenster eine Analyse starten.)"
                    : $"[{last.CreatedAt:dd.MM. HH:mm}] {last.Profile} · {last.Model}\n\n{last.Answer}";
            }
            catch (Exception ex)
            {
                txtSupervisor.Text = $"Supervisor-Berichte n/v: {ex.Message}";
            }
        }

        // ===== Helfer =====

        private static string ShortTime(string iso) =>
            DateTime.TryParse(iso, out var dt) ? dt.ToString("HH:mm:ss") : iso;

        private static string OneLine(string s) =>
            (s ?? string.Empty).Replace("\r", " ").Replace("\n", " ").Trim();
    }
}
