using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using PolyTrader.Core.Analytics;
using PolyTrader.Core.Modularity;
using PolyTrader.Core.Persistence;
using PolyTraderSharp.Models;

namespace PolyTraderSharp.Ui.Views
{
    /// <summary>
    /// Modulübergreifendes Dashboard aus dem generischen Core-Trade-Log (ITradeLogRepository).
    /// Tab „Dashboard": KPI-Kacheln + Charts (Equity-Kurve, PnL je Modul, PnL je Tag) für den im
    /// ToolStrip gewählten Scope (Konto/Modul/Live-Demo/Zeitraum). Tab „Tradehistorie": gefilterte
    /// Trade-Liste. Auswertungslogik pur in <see cref="TradeAnalytics"/>; Charts via ScottPlot
    /// (als Bitmap gerendert – kein WinForms-GL-Control, saubere Dependencies).
    /// </summary>
    public partial class DashboardView : Form
    {
        private ITradeLogRepository? _tradeLog;
        private TradingState? _state;

        private IReadOnlyList<ModuleActivationInfo>? _moduleInfos;   // alle Module (auch deaktivierte)
        private string? _settingsPath;                               // Quelle/Ziel der Modul-Aktivierung

        private List<TradeRecord> _allTrades = new();      // zuletzt geladener Roh-Satz
        private List<DashboardTradeRow> _historyBase = new(); // Scope-gefiltert, Basis für die Historie-Filter
        private bool _loading;

        public DashboardView()
        {
            InitializeComponent();

            colEntry.DefaultCellStyle.Format = "F3";
            colExit.DefaultCellStyle.Format = "F3";
            colSize.DefaultCellStyle.Format = "F2";
            colPnl.DefaultCellStyle.Format = "F2";
            colPnlPct.DefaultCellStyle.Format = "F1";
            colClosedAt.DefaultCellStyle.Format = "dd.MM.yyyy HH:mm";

            cbMode.Items.AddRange(new object[] { "Alle", "Live", "Demo" });
            cbRange.Items.AddRange(new object[] { "7 Tage", "30 Tage", "90 Tage", "Alle" });
            cbWinLoss.Items.AddRange(new object[] { "Alle", "Gewinner", "Verlierer" });

            tsRefresh.Click += (_, _) => RefreshData();
            cbAccount.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyScope(); };
            cbModule.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyScope(); };
            cbMode.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyScope(); };
            cbRange.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyScope(); };
            tbSearch.TextChanged += (_, _) => { if (!_loading) ApplyHistoryFilter(); };
            cbWinLoss.SelectedIndexChanged += (_, _) => { if (!_loading) ApplyHistoryFilter(); };

            dgvModules.CellContentClick += Modules_CellContentClick;
        }

        /// <param name="modules">
        /// Alle bekannten Module (auch deaktivierte) für den Tab „Module". <c>null</c> = kein
        /// Modul-Management (Tab wird entfernt, z.B. im Headless-Smoke-Test).
        /// </param>
        /// <param name="settingsPath">Pfad der Server-Settings-Datei (Ziel für die Ein/Aus-Persistenz).</param>
        public void Initialize(ITradeLogRepository tradeLog, TradingState state,
            IReadOnlyList<ModuleActivationInfo>? modules = null, string? settingsPath = null)
        {
            _tradeLog = tradeLog;
            _state = state;
            _moduleInfos = modules;
            _settingsPath = settingsPath;
            RefreshData();
            InitModulesTab();
        }

        // ===== 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()
        {
            // Konten
            var selectedAcc = (cbAccount.SelectedItem as FilterAccount)?.Id ?? -1;
            cbAccount.Items.Clear();
            cbAccount.Items.Add(new FilterAccount(-1, "Alle Konten"));
            if (_state != null)
                foreach (var a in _state.Accounts.Values.OrderBy(a => a.AccountId))
                    cbAccount.Items.Add(new FilterAccount(a.AccountId,
                        (string.IsNullOrEmpty(a.Name) ? $"#{a.AccountId}" : a.Name) + (a.IsDemo ? " (Demo)" : "")));
            cbAccount.SelectedIndex = Math.Max(0, IndexOfAccount(selectedAcc));

            // Module (aus den vorhandenen Daten)
            string selectedMod = cbModule.SelectedItem as string ?? "Alle Module";
            cbModule.Items.Clear();
            cbModule.Items.Add("Alle Module");
            foreach (var m in _allTrades.Select(t => t.ModuleName).Where(m => !string.IsNullOrEmpty(m)).Distinct().OrderBy(m => m))
                cbModule.Items.Add(m);
            int modIdx = cbModule.Items.IndexOf(selectedMod);
            cbModule.SelectedIndex = modIdx >= 0 ? modIdx : 0;

            if (cbMode.SelectedIndex < 0) cbMode.SelectedIndex = 0;      // Alle
            if (cbRange.SelectedIndex < 0) cbRange.SelectedIndex = 1;    // 30 Tage
            if (cbWinLoss.SelectedIndex < 0) cbWinLoss.SelectedIndex = 0;
        }

        private int IndexOfAccount(int id)
        {
            for (int i = 0; i < cbAccount.Items.Count; i++)
                if (cbAccount.Items[i] is FilterAccount fa && fa.Id == id) return i;
            return 0;
        }

        private void ApplyScope()
        {
            int accId = (cbAccount.SelectedItem as FilterAccount)?.Id ?? -1;
            string module = cbModule.SelectedItem as string ?? "Alle Module";
            string mode = cbMode.SelectedItem as string ?? "Alle";
            int? days = (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);
            RenderCharts(scoped);

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

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

        private void UpdateKpis(List<TradeRecord> scoped)
        {
            var k = TradeAnalytics.ComputeKpis(scoped);
            lblKpiPnl.Text = $"Netto-PnL\n{k.NetPnl:N2} USDC";
            lblKpiPnl.ForeColor = k.NetPnl >= 0 ? System.Drawing.Color.ForestGreen : System.Drawing.Color.Firebrick;
            lblKpiWin.Text = $"Winrate\n{k.WinRatePct:N1} %";
            lblKpiTrades.Text = $"Trades\n{k.TradeCount}";
            lblKpiAvg.Text = $"Ø PnL/Trade\n{k.AvgPnlPerTrade:N2}";
            lblKpiPf.Text = $"Profit-Faktor\n{(k.ProfitFactor >= TradeAnalytics.NoLossProfitFactor ? "∞" : k.ProfitFactor.ToString("N2"))}";
        }

        // ===== Charts (ScottPlot -> Bitmap) =====

        private void RenderCharts(List<TradeRecord> scoped)
        {
            RenderPlot(picEquity, plot =>
            {
                var curve = TradeAnalytics.EquityCurve(scoped);
                plot.Title("Equity-Kurve (kumulierter PnL)");
                if (curve.Count == 0) return;
                double[] xs = curve.Select(p => p.At.ToOADate()).ToArray();
                double[] ys = curve.Select(p => (double)p.Cumulative).ToArray();
                plot.Add.Scatter(xs, ys);
                plot.Axes.DateTimeTicksBottom();
            });

            RenderPlot(picModule, plot =>
            {
                var byMod = TradeAnalytics.PnlByKey(scoped, t => string.IsNullOrEmpty(t.ModuleName) ? "—" : t.ModuleName);
                plot.Title("PnL je Modul");
                if (byMod.Count == 0) return;
                plot.Add.Bars(byMod.Select(x => (double)x.Pnl).ToArray());
                SetCategoryTicks(plot, byMod.Select(x => x.Key).ToArray());
            });

            RenderPlot(picDay, plot =>
            {
                var byDay = TradeAnalytics.PnlByDay(scoped);
                plot.Title("PnL je Tag");
                if (byDay.Count == 0) return;
                plot.Add.Bars(byDay.Select(x => (double)x.Pnl).ToArray());
                SetCategoryTicks(plot, byDay.Select(x => x.Day.ToString("dd.MM")).ToArray());
            });
        }

        private static void SetCategoryTicks(ScottPlot.Plot plot, string[] labels)
        {
            var ticks = new ScottPlot.TickGenerators.NumericManual();
            for (int i = 0; i < labels.Length; i++)
                ticks.AddMajor(i, labels[i]);
            plot.Axes.Bottom.TickGenerator = ticks;
        }

        private static void RenderPlot(PictureBox pic, Action<ScottPlot.Plot> build)
        {
            int w = Math.Max(pic.ClientSize.Width, 300);
            int h = Math.Max(pic.ClientSize.Height, 200);
            var plot = new ScottPlot.Plot();
            try { build(plot); }
            catch { /* Chart-Rendering darf die UI nie killen */ }

            byte[] png = plot.GetImage(w, h).GetImageBytes();
            using var ms = new MemoryStream(png);
            using var img = System.Drawing.Image.FromStream(ms);
            var old = pic.Image;
            pic.Image = new System.Drawing.Bitmap(img);
            old?.Dispose();
        }

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

        private void ApplyHistoryFilter()
        {
            string search = tbSearch.Text.Trim();
            string winLoss = 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);

            dgvTrades.DataSource = new BindingList<DashboardTradeRow>(rows.ToList());
        }

        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()
        {
            if (_moduleInfos == null || string.IsNullOrEmpty(_settingsPath))
            {
                // Kein Modul-Management (z.B. Smoke-Test) -> Tab entfernen statt leer anzuzeigen.
                if (tabControlDash.TabPages.Contains(tabModules))
                    tabControlDash.TabPages.Remove(tabModules);
                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();

            var rows = new List<ModuleRow>();
            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";
                }

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

            dgvModules.DataSource = new BindingList<ModuleRow>(rows);
        }

        private void Modules_CellContentClick(object? sender, DataGridViewCellEventArgs e)
        {
            if (_moduleInfos == null || string.IsNullOrEmpty(_settingsPath)) return;
            if (e.RowIndex < 0 || e.ColumnIndex < 0) return;
            if (dgvModules.Columns[e.ColumnIndex].Name != "colModAction") return;
            if (dgvModules.Rows[e.RowIndex].DataBoundItem is not ModuleRow row) return;

            var info = _moduleInfos.FirstOrDefault(m => m.Name == row.Modul);
            if (info == null) return;

            if (!string.IsNullOrEmpty(info.Blocker))
            {
                MessageBox.Show($"Modul „{info.Name}“ kann nicht aktiviert werden:\n\n{info.Blocker}",
                    "Aktivierung nicht möglich", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

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

                bool nowDisabled;
                if (set.Contains(info.Name)) { set.Remove(info.Name); nowDisabled = false; }
                else { set.Add(info.Name); nowDisabled = true; }

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

                PopulateModules();

                MessageBox.Show(
                    $"Modul „{info.Name}“ wird beim nächsten Start {(nowDisabled ? "NICHT mehr geladen" : "geladen")}.\n\n" +
                    "Die Änderung greift erst nach einem Neustart von PolyTrader.",
                    "Gespeichert", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"Konnte die Modul-Einstellung nicht speichern: {ex.Message}", "Fehler",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>Anzeige-Zeile des Modul-Grids (Bindung über DataPropertyName).</summary>
        private sealed class ModuleRow
        {
            public string Modul { get; set; } = string.Empty;
            public string Status { get; set; } = string.Empty;
            public string Hinweis { get; set; } = string.Empty;
            public string Aktion { get; set; } = string.Empty;
        }
    }

    /// <summary>Anzeige-Zeile für das Historie-Grid (Account bereits zu Name aufgelöst).</summary>
    public class DashboardTradeRow
    {
        public string Module { get; set; } = string.Empty;
        public string Account { get; set; } = string.Empty;
        public string Market { get; set; } = string.Empty;
        public string Outcome { get; set; } = string.Empty;
        public string Side { get; set; } = string.Empty;
        public decimal EntryPrice { get; set; }
        public decimal ExitPrice { get; set; }
        public decimal Size { get; set; }
        public decimal RealizedPnl { get; set; }
        public decimal PnlPercent { get; set; }
        public DateTime ClosedAt { get; set; }
    }
}
