using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using PolyTrader.App.Avalonia.ViewModels;
using PolyTrader.Core.Modularity;
using PolyTrader.Core.Persistence;
using PolyTraderSharp;
using PolyTraderSharp.Models;

namespace PolyTrader.App.Avalonia.Views
{
    /// <summary>
    /// Modulübergreifendes Dashboard aus dem Core-Trade-Log. Aufbau und Verhalten entsprechen der
    /// bisherigen WinForms-Ansicht; die Auswertung kommt unverändert aus <c>TradeAnalytics</c>,
    /// die Diagramme jetzt aus <see cref="DashboardChartsModel"/> (LiveCharts2).
    /// Layout vollständig in DashboardWindow.axaml.
    /// </summary>
    public partial class DashboardWindow : UserControl
    {
        private readonly DashboardChartsModel _charts = new();
        private readonly ObservableCollection<DashboardTradeRow> _history = new();
        private readonly ObservableCollection<ModuleRow> _modules = new();

        private ITradeLogRepository? _tradeLog;
        private TradingState? _state;
        private IReadOnlyList<ModuleActivationInfo>? _moduleInfos;
        private string? _settingsPath;

        private List<TradeRecord> _allTrades = new();
        private List<DashboardTradeRow> _historyBase = new();
        private bool _loading;

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

        /// <param name="moduleInfos">
        /// Alle bekannten Module (auch deaktivierte) für den Tab „Module". <c>null</c> = kein
        /// Modul-Management (Tab wird ausgeblendet, z.B. im Smoke-Test).
        /// </param>
        public DashboardWindow(IModuleUiHost host, ITradeLogRepository tradeLog, TradingState state,
            IReadOnlyList<ModuleActivationInfo>? moduleInfos = null, string? settingsPath = null) : this()
        {
            _tradeLog = tradeLog;
            _state = state;
            _moduleInfos = moduleInfos;
            _settingsPath = settingsPath;

            DataContext = _charts;
            this.FindControl<DataGrid>("gridTrades")!.ItemsSource = _history;
            this.FindControl<DataGrid>("gridModules")!.ItemsSource = _modules;

            var cbMode = this.FindControl<ComboBox>("cbMode")!;
            var cbRange = this.FindControl<ComboBox>("cbRange")!;
            var cbWinLoss = this.FindControl<ComboBox>("cbWinLoss")!;
            cbMode.ItemsSource = new[] { "Alle", "Live", "Demo" };
            cbRange.ItemsSource = new[] { "7 Tage", "30 Tage", "90 Tage", "Alle" };
            cbWinLoss.ItemsSource = new[] { "Alle", "Gewinner", "Verlierer" };
            cbMode.SelectedIndex = 0;
            cbRange.SelectedIndex = 1;      // 30 Tage, wie bisher
            cbWinLoss.SelectedIndex = 0;

            this.FindControl<Button>("btnRefresh")!.Click += (_, _) => RefreshData();
            foreach (var name in new[] { "cbAccount", "cbModule", "cbMode", "cbRange" })
                this.FindControl<ComboBox>(name)!.SelectionChanged += (_, _) => { if (!_loading) ApplyScope(); };

            this.FindControl<TextBox>("tbSearch")!.TextChanged += (_, _) => { if (!_loading) ApplyHistoryFilter(); };
            cbWinLoss.SelectionChanged += (_, _) => { if (!_loading) ApplyHistoryFilter(); };

            RefreshData();
            InitModulesTab();

            // Die KPI-Farbe wird im Code gesetzt und wechselt daher nicht von selbst mit dem Thema.
            // Kein Abmelden mehr: Seiten werden in der Einfenster-Shell einmal erzeugt und leben
            // bis zum Programmende (siehe AvaloniaUiHost) – ein Gegenstück zu Window.Closed gibt es
            // hier nicht, und Unloaded würde bei jedem Seitenwechsel feuern.
            Shell.ThemeManager.ThemeChanged += ApplyScope;
        }

        // ===== Daten laden / Scope =====

        private void RefreshData()
        {
            if (_tradeLog == null) return;
            try { _allTrades = _tradeLog.GetRecent(5000); }
            catch { _allTrades = new List<TradeRecord>(); }   // DB nicht bereit -> leer statt Absturz

            _loading = true;
            PopulateScopeCombos();
            _loading = false;

            ApplyScope();
        }

        private void PopulateScopeCombos()
        {
            var cbAccount = this.FindControl<ComboBox>("cbAccount")!;
            var cbModule = this.FindControl<ComboBox>("cbModule")!;

            int selectedAcc = (cbAccount.SelectedItem as FilterAccount)?.Id ?? -1;
            var accounts = new List<FilterAccount> { new(-1, "Alle Konten") };
            if (_state != null)
                accounts.AddRange(_state.Accounts.Values.OrderBy(a => a.AccountId)
                    .Select(a => new FilterAccount(a.AccountId,
                        (string.IsNullOrEmpty(a.Name) ? $"#{a.AccountId}" : a.Name) + (a.IsDemo ? " (Demo)" : ""))));
            cbAccount.ItemsSource = accounts;
            int accIdx = accounts.FindIndex(a => a.Id == selectedAcc);
            cbAccount.SelectedIndex = accIdx >= 0 ? accIdx : 0;

            string selectedMod = cbModule.SelectedItem as string ?? "Alle Module";
            var mods = new List<string> { "Alle Module" };
            mods.AddRange(_allTrades.Select(t => t.ModuleName)
                                    .Where(m => !string.IsNullOrEmpty(m))
                                    .Distinct().OrderBy(m => m));
            cbModule.ItemsSource = mods;
            int modIdx = mods.IndexOf(selectedMod);
            cbModule.SelectedIndex = modIdx >= 0 ? modIdx : 0;
        }

        private void ApplyScope()
        {
            int accId = (this.FindControl<ComboBox>("cbAccount")!.SelectedItem as FilterAccount)?.Id ?? -1;
            string module = this.FindControl<ComboBox>("cbModule")!.SelectedItem as string ?? "Alle Module";
            string mode = this.FindControl<ComboBox>("cbMode")!.SelectedItem as string ?? "Alle";
            int? days = this.FindControl<ComboBox>("cbRange")!.SelectedItem as string switch
            {
                "7 Tage" => 7,
                "30 Tage" => 30,
                "90 Tage" => 90,
                _ => (int?)null
            };
            DateTime? since = days.HasValue ? DateTime.UtcNow.AddDays(-days.Value) : null;

            var scoped = _allTrades.Where(t =>
                (accId < 0 || t.AccountId == accId) &&
                (module == "Alle Module" || t.ModuleName == module) &&
                (mode == "Alle" || (mode == "Live" && !t.IsDemo) || (mode == "Demo" && t.IsDemo)) &&
                (!since.HasValue || t.ClosedAt >= since.Value)).ToList();

            UpdateKpis(scoped);
            _charts.Update(scoped);

            _historyBase = scoped.OrderByDescending(t => t.ClosedAt).Select(ToRow).ToList();
            ApplyHistoryFilter();
        }

        // ===== KPIs =====

        private void UpdateKpis(List<TradeRecord> scoped)
        {
            var k = Core.Analytics.TradeAnalytics.ComputeKpis(scoped);

            var pnl = this.FindControl<TextBlock>("kpiPnl")!;
            pnl.Text = $"{k.NetPnl:N2} USDC";
            pnl.Foreground = Shell.ThemeManager.Brush(k.NetPnl >= 0 ? "AppPositiveBrush" : "AppNegativeBrush");

            this.FindControl<TextBlock>("kpiWin")!.Text = $"{k.WinRatePct:N1} %";
            this.FindControl<TextBlock>("kpiTrades")!.Text = k.TradeCount.ToString();
            this.FindControl<TextBlock>("kpiAvg")!.Text = $"{k.AvgPnlPerTrade:N2}";

            // Verlustfreies Set wird als ∞ dargestellt (wie bisher), nicht als 999.
            this.FindControl<TextBlock>("kpiPf")!.Text =
                k.ProfitFactor >= Core.Analytics.TradeAnalytics.NoLossProfitFactor
                    ? "∞"
                    : k.ProfitFactor.ToString("N2");
        }

        // ===== Tradehistorie =====

        private void ApplyHistoryFilter()
        {
            string search = this.FindControl<TextBox>("tbSearch")!.Text?.Trim() ?? string.Empty;
            string winLoss = this.FindControl<ComboBox>("cbWinLoss")!.SelectedItem as string ?? "Alle";

            IEnumerable<DashboardTradeRow> rows = _historyBase;
            if (search.Length > 0)
                rows = rows.Where(r =>
                    (r.Market?.Contains(search, StringComparison.OrdinalIgnoreCase) ?? false) ||
                    (r.Outcome?.Contains(search, StringComparison.OrdinalIgnoreCase) ?? false));
            if (winLoss == "Gewinner") rows = rows.Where(r => r.RealizedPnl > 0m);
            else if (winLoss == "Verlierer") rows = rows.Where(r => r.RealizedPnl < 0m);

            _history.Clear();
            foreach (var r in rows) _history.Add(r);
        }

        private DashboardTradeRow ToRow(TradeRecord r) => new()
        {
            Module = r.ModuleName,
            Account = ResolveAccount(r.AccountId, r.IsDemo),
            Market = r.MarketQuestion,
            Outcome = r.Outcome,
            Side = r.Side,
            EntryPrice = r.EntryPrice,
            ExitPrice = r.ExitPrice,
            Size = r.Size,
            RealizedPnl = r.RealizedPnl,
            PnlPercent = r.PnlPercent,
            ClosedAt = r.ClosedAt
        };

        private string ResolveAccount(int accountId, bool isDemo)
        {
            string suffix = isDemo ? " (Demo)" : "";
            if (_state != null && _state.Accounts.TryGetValue(accountId, out var acc) && !string.IsNullOrEmpty(acc.Name))
                return acc.Name + suffix;
            return $"#{accountId}{suffix}";
        }

        private sealed record FilterAccount(int Id, string Label)
        {
            public override string ToString() => Label;
        }

        // ===== Module aktivieren/deaktivieren (neustart-basiert) =====

        private void InitModulesTab()
        {
            var tab = this.FindControl<TabItem>("tabModules")!;
            if (_moduleInfos == null || string.IsNullOrEmpty(_settingsPath))
            {
                tab.IsVisible = false;   // kein Modul-Management (z.B. Smoke-Test)
                return;
            }
            PopulateModules();
        }

        private HashSet<string> LoadDisabledModules()
        {
            try
            {
                var s = ServerSettings.Load(_settingsPath!);
                return new HashSet<string>(s.DisabledModules, StringComparer.OrdinalIgnoreCase);
            }
            catch
            {
                return new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            }
        }

        private void PopulateModules()
        {
            if (_moduleInfos == null) return;
            var disabled = LoadDisabledModules();

            _modules.Clear();
            foreach (var m in _moduleInfos)
            {
                bool blocked = !string.IsNullOrEmpty(m.Blocker);
                bool desiredEnabled = !disabled.Contains(m.Name);

                string status, hint, action;
                if (blocked)
                {
                    status = "⚠ Nicht aktivierbar"; hint = m.Blocker!; action = "—";
                }
                else if (m.IsRunning && desiredEnabled)
                {
                    status = "Aktiv"; hint = ""; action = "Deaktivieren";
                }
                else if (m.IsRunning && !desiredEnabled)
                {
                    status = "Aktiv – stoppt nach Neustart"; hint = "Änderung greift nach Neustart"; action = "Aktivieren";
                }
                else if (!m.IsRunning && desiredEnabled)
                {
                    status = "Startet nach Neustart"; hint = "Änderung greift nach Neustart"; action = "Deaktivieren";
                }
                else
                {
                    status = "Deaktiviert"; hint = ""; action = "Aktivieren";
                }

                _modules.Add(new ModuleRow
                {
                    Modul = m.Name, Status = status, Hinweis = hint, Aktion = action, CanToggle = !blocked
                });
            }
        }

        private void OnModuleActionClick(object? sender, RoutedEventArgs e)
        {
            if (_moduleInfos == null || string.IsNullOrEmpty(_settingsPath)) return;
            if (sender is not Button { DataContext: ModuleRow row }) return;

            var info = _moduleInfos.FirstOrDefault(m => m.Name == row.Modul);
            if (info == null || !string.IsNullOrEmpty(info.Blocker)) return;

            try
            {
                // Frisch aus der Datei lesen, damit parallele Änderungen nicht überschrieben werden.
                var settings = ServerSettings.Load(_settingsPath!);
                var set = new HashSet<string>(settings.DisabledModules, StringComparer.OrdinalIgnoreCase);

                if (set.Contains(info.Name)) set.Remove(info.Name);
                else set.Add(info.Name);

                settings.DisabledModules = set.OrderBy(x => x).ToList();
                settings.Save(_settingsPath!);

                PopulateModules();
            }
            catch (Exception ex)
            {
                // Fehler landet im Status der Zeile statt in einem Dialog – die Ansicht bleibt bedienbar.
                _modules.Clear();
                _modules.Add(new ModuleRow
                {
                    Modul = info.Name,
                    Status = "Speichern fehlgeschlagen",
                    Hinweis = ex.Message,
                    Aktion = "—",
                    CanToggle = false
                });
            }
        }
    }
}
