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

namespace PolyTrader.App.Avalonia.Views.Modules
{
    /// <summary>
    /// Copytrading-Fenster mit vier Tabs: Master-Trader, Offene Trades, Geschlossene Trades,
    /// Account-Einstellungen. Layout vollständig in CopyTradingWindow.axaml.
    /// </summary>
    public partial class CopyTradingWindow : UserControl
    {
        private readonly ObservableCollection<TrackedTrader> _traders = new();
        private readonly ObservableCollection<AccountAssignment> _assignments = new();
        private readonly ObservableCollection<CopyOpenTradeRow> _openTrades = new();
        private readonly ObservableCollection<CopyClosedTradeRow> _closedTrades = new();

        private readonly TradingState _state;
        private readonly CopyTradingState _copyState;
        private readonly ITrackedTraderRepository _traderRepo;
        private readonly ICopyTradingAccountSettingsRepository _settingsRepo;
        private readonly ICopyTradeLogRepository _tradeLog;

        private List<CopyClosedTradeRow> _closedBase = new();
        private bool _loading;

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

        public CopyTradingWindow(IModuleUiHost host, TradingState state, CopyTradingState copyState,
            ITrackedTraderRepository traderRepo, ICopyTradingAccountSettingsRepository settingsRepo,
            ICopyTradeLogRepository tradeLog) : this()
        {
            _state = state;
            _copyState = copyState;
            _traderRepo = traderRepo;
            _settingsRepo = settingsRepo;
            _tradeLog = tradeLog;

            // --- Master-Trader ---
            var gridTraders = this.FindControl<DataGrid>("gridTraders")!;
            gridTraders.ItemsSource = _traders;
            gridTraders.SelectionChanged += (_, _) => ShowSelectedTrader();
            this.FindControl<ListBox>("listAccounts")!.ItemsSource = _assignments;
            this.FindControl<Button>("btnTraderRefresh")!.Click += (_, _) => LoadTraders();
            this.FindControl<Button>("btnTraderNew")!.Click += (_, _) => AddNewTrader();
            this.FindControl<Button>("btnTraderSave")!.Click += (_, _) => SaveSelectedTrader();
            this.FindControl<Button>("btnTraderDelete")!.Click += (_, _) => DeleteSelectedTrader();

            // --- Offene Trades ---
            var gridOpen = this.FindControl<DataGrid>("gridOpen")!;
            gridOpen.ItemsSource = _openTrades;
            // Zeilenfärbung nach Buchgewinn: die Kategorie kommt aus der getesteten TradeRowColoring,
            // die Farbe aus TradeRowPalette. LoadingRow greift auch bei virtualisierten Zeilen.
            gridOpen.LoadingRow += (_, e) =>
            {
                if (e.Row.DataContext is CopyOpenTradeRow row) e.Row.Background = row.RowBrush;
            };
            this.FindControl<Button>("btnOpenRefresh")!.Click += (_, _) => LoadOpenTrades();

            // --- Geschlossene Trades ---
            var gridClosed = this.FindControl<DataGrid>("gridClosed")!;
            gridClosed.ItemsSource = _closedTrades;
            gridClosed.LoadingRow += (_, e) =>
            {
                if (e.Row.DataContext is CopyClosedTradeRow row) e.Row.Background = row.RowBrush;
            };
            var cbResult = this.FindControl<ComboBox>("cbClosedResult")!;
            cbResult.ItemsSource = new[] { "Alle", "Gewinner", "Verlierer" };
            cbResult.SelectedIndex = 0;
            this.FindControl<Button>("btnClosedRefresh")!.Click += (_, _) => LoadClosedTrades();
            this.FindControl<Button>("btnClosedReset")!.Click += (_, _) => ResetClosedFilter();
            this.FindControl<TextBox>("tbClosedMarket")!.TextChanged += (_, _) => { if (!_loading) ApplyClosedFilter(); };
            foreach (var n in new[] { "cbClosedMaster", "cbClosedResult" })
                this.FindControl<ComboBox>(n)!.SelectionChanged += (_, _) => { if (!_loading) ApplyClosedFilter(); };
            foreach (var n in new[] { "dtClosedFrom", "dtClosedTo" })
                this.FindControl<CalendarDatePicker>(n)!.SelectedDateChanged += (_, _) => { if (!_loading) ApplyClosedFilter(); };

            // --- Account-Einstellungen ---
            var cbAcc = this.FindControl<ComboBox>("cbSettingsAccount")!;
            cbAcc.SelectionChanged += (_, _) => ShowAccountSettings();
            this.FindControl<Button>("btnSettingsSave")!.Click += (_, _) => SaveAccountSettings();

            LoadTraders();
            LoadOpenTrades();
            LoadClosedTrades();
            LoadAccountList();

            // Zeilenhintergruende werden in LoadingRow gesetzt und wechseln daher nicht von selbst
            // mit dem Thema. Ein Neuzuweisen der Quelle laesst die Zeilen neu erzeugen.
            void OnTheme()
            {
                gridOpen.ItemsSource = null; gridOpen.ItemsSource = _openTrades;
                gridClosed.ItemsSource = null; gridClosed.ItemsSource = _closedTrades;
            }
            // Kein Abmelden: Seiten leben in der Einfenster-Shell bis zum Programmende
            // (siehe AvaloniaUiHost), ein Gegenstueck zu Window.Closed gibt es nicht.
            Shell.ThemeManager.ThemeChanged += OnTheme;
        }

        // ===== Master-Trader =====

        private void LoadTraders()
        {
            _traders.Clear();
            foreach (var t in _copyState.Traders.Values.OrderBy(t => t.Id)) _traders.Add(t);

            var grid = this.FindControl<DataGrid>("gridTraders")!;
            if (_traders.Count > 0 && grid.SelectedItem == null) grid.SelectedIndex = 0;
        }

        private void ShowSelectedTrader()
        {
            var trader = this.FindControl<DataGrid>("gridTraders")!.SelectedItem as TrackedTrader;
            this.FindControl<SettingsEditor>("editorTrader")!.Show(trader);

            _assignments.Clear();
            if (trader == null) return;

            foreach (var acc in _state.Accounts.Values.OrderBy(a => a.AccountId))
                _assignments.Add(new AccountAssignment
                {
                    AccountId = acc.AccountId,
                    Label = $"#{acc.AccountId} {acc.Name}{(acc.IsDemo ? " (Demo)" : "")}",
                    IsAssigned = trader.AssignedAccountIds.Contains(acc.AccountId)
                });
        }

        private void SaveSelectedTrader()
        {
            if (this.FindControl<DataGrid>("gridTraders")!.SelectedItem is not TrackedTrader trader) return;

            trader.AssignedAccountIds = _assignments.Where(a => a.IsAssigned).Select(a => a.AccountId).ToHashSet();
            _traderRepo.Update(trader);
            _copyState.Traders[trader.Id] = trader;   // Hot-Path-State synchron halten

            this.FindControl<TextBlock>("lblTraderHint")!.Text =
                $"Gespeichert: #{trader.Id} {trader.DisplayName} ({trader.AssignedAccountIds.Count} Account(s)) um {DateTime.Now:HH:mm:ss}.";
        }

        private void AddNewTrader()
        {
            int nextId = _traders.Count > 0 ? _traders.Max(t => t.Id) + 1 : 1;
            var trader = new TrackedTrader { Id = nextId, DisplayName = $"Neuer Trader {nextId}" };
            _traders.Add(trader);

            var grid = this.FindControl<DataGrid>("gridTraders")!;
            grid.SelectedItem = trader;
            this.FindControl<TextBlock>("lblTraderHint")!.Text = $"Neuer Master-Trader #{nextId} – Felder ausfüllen und Speichern.";
        }

        private async void DeleteSelectedTrader()
        {
            var grid = this.FindControl<DataGrid>("gridTraders")!;
            if (grid.SelectedItem is not TrackedTrader trader)
            {
                this.FindControl<TextBlock>("lblTraderHint")!.Text = "Kein Trader ausgewählt.";
                return;
            }

            int id = trader.Id;
            bool confirm = await DialogWindow.Confirm(this, "Löschen bestätigen",
                $"Master-Trader #{id} ({trader.DisplayName}) wirklich löschen?");
            if (!confirm) return;

            _traderRepo.Delete(id);
            _copyState.Traders.TryRemove(id, out _);
            _traders.Remove(trader);
            ShowSelectedTrader();

            this.FindControl<TextBlock>("lblTraderHint")!.Text = $"Master-Trader #{id} gelöscht.";
        }

        // ===== Offene Trades =====

        private void LoadOpenTrades()
        {
            _openTrades.Clear();

            foreach (var acc in _state.Accounts.Values.OrderBy(a => a.AccountId))
            foreach (var p in acc.OpenPositions.Values.OrderByDescending(p => p.OpenedAt))
            {
                decimal unrealized = p.CurrentValueUsd - p.AmountUsd;
                decimal pct = p.AmountUsd > 0m ? 100m * unrealized / p.AmountUsd : 0m;

                _openTrades.Add(new CopyOpenTradeRow
                {
                    Account = acc.Name + (acc.IsDemo ? " (Demo)" : ""),
                    Trader = string.IsNullOrEmpty(p.SourceTraderName) ? $"#{p.SourceTraderId}" : p.SourceTraderName,
                    Market = p.MarketQuestion,
                    Outcome = p.Outcome,
                    Side = p.Side,
                    Entry = p.EntryPrice,
                    Current = p.CurrentPrice,
                    Size = p.Size,
                    Amount = p.AmountUsd,
                    Value = p.CurrentValueUsd,
                    Unrealized = unrealized,
                    UnrealizedPct = pct,
                    Status = p.ExitPending ? "Exit läuft" : "Offen"
                });
            }

            decimal totalAmount = _openTrades.Sum(r => r.Amount);
            decimal totalUnreal = _openTrades.Sum(r => r.Unrealized);
            this.FindControl<TextBlock>("lblOpenSummary")!.Text =
                $"{_openTrades.Count} offene Position(en) · Einsatz {totalAmount:N2} USDC · Buchgewinn {totalUnreal:N2} USDC";
        }

        // ===== Geschlossene Trades =====

        private void LoadClosedTrades()
        {
            try
            {
                var trades = _tradeLog.Find(_ => true);

                _closedBase = trades
                    .OrderByDescending(t => t.ClosedAt)
                    .Select(t => new CopyClosedTradeRow
                    {
                        TradeId = t.TradeId,
                        Account = ResolveAccount(t.AccountId, t.IsDemo),
                        Trader = ResolveTrader(t.SourceTraderId),
                        Market = t.MarketQuestion,
                        Outcome = t.Outcome,
                        Side = t.Side,
                        Entry = t.EntryPrice,
                        Exit = t.ExitPrice,
                        Size = t.Size,
                        Pnl = t.RealizedPnl,
                        PnlPercent = t.PnlPercent,
                        OpenedAt = t.OpenedAt,
                        ClosedAt = t.ClosedAt
                    })
                    .ToList();
            }
            catch
            {
                _closedBase = new List<CopyClosedTradeRow>();   // DB nicht bereit -> leer statt Absturz
            }

            _loading = true;
            var cbMaster = this.FindControl<ComboBox>("cbClosedMaster")!;
            var masters = new List<string> { "Alle Master" };
            masters.AddRange(_closedBase.Select(r => r.Trader).Distinct().OrderBy(x => x));
            cbMaster.ItemsSource = masters;
            cbMaster.SelectedIndex = 0;
            _loading = false;

            ApplyClosedFilter();
        }

        /// <summary>Kontoname aus dem State; fällt auf die ID zurück, wenn das Konto fehlt.</summary>
        private string ResolveAccount(int accountId, bool isDemo)
        {
            string suffix = isDemo ? " (Demo)" : "";
            return _state.Accounts.TryGetValue(accountId, out var acc) && !string.IsNullOrEmpty(acc.Name)
                ? acc.Name + suffix
                : $"#{accountId}{suffix}";
        }

        private string ResolveTrader(int traderId) =>
            _copyState.Traders.TryGetValue(traderId, out var t) && !string.IsNullOrEmpty(t.DisplayName)
                ? t.DisplayName
                : $"#{traderId}";

        private void ResetClosedFilter()
        {
            _loading = true;
            this.FindControl<TextBox>("tbClosedMarket")!.Text = string.Empty;
            this.FindControl<ComboBox>("cbClosedMaster")!.SelectedIndex = 0;
            this.FindControl<ComboBox>("cbClosedResult")!.SelectedIndex = 0;
            this.FindControl<CalendarDatePicker>("dtClosedFrom")!.SelectedDate = null;
            this.FindControl<CalendarDatePicker>("dtClosedTo")!.SelectedDate = null;
            _loading = false;
            ApplyClosedFilter();
        }

        private void ApplyClosedFilter()
        {
            string market = this.FindControl<TextBox>("tbClosedMarket")!.Text?.Trim() ?? string.Empty;
            string master = this.FindControl<ComboBox>("cbClosedMaster")!.SelectedItem as string ?? "Alle Master";
            string result = this.FindControl<ComboBox>("cbClosedResult")!.SelectedItem as string ?? "Alle";
            DateTime? from = this.FindControl<CalendarDatePicker>("dtClosedFrom")!.SelectedDate;
            DateTime? to = this.FindControl<CalendarDatePicker>("dtClosedTo")!.SelectedDate;

            IEnumerable<CopyClosedTradeRow> rows = _closedBase;
            if (market.Length > 0)
                rows = rows.Where(r => r.Market.Contains(market, StringComparison.OrdinalIgnoreCase));
            if (master != "Alle Master") rows = rows.Where(r => r.Trader == master);
            if (result == "Gewinner") rows = rows.Where(r => r.Pnl > 0m);
            else if (result == "Verlierer") rows = rows.Where(r => r.Pnl < 0m);
            if (from.HasValue) rows = rows.Where(r => r.ClosedAt >= from.Value.Date);
            if (to.HasValue) rows = rows.Where(r => r.ClosedAt < to.Value.Date.AddDays(1));

            var list = rows.ToList();
            _closedTrades.Clear();
            foreach (var r in list) _closedTrades.Add(r);

            decimal pnl = list.Sum(r => r.Pnl);
            int wins = list.Count(r => r.Pnl > 0m);
            decimal winrate = list.Count > 0 ? 100m * wins / list.Count : 0m;
            this.FindControl<TextBlock>("lblClosedSummary")!.Text =
                $"{list.Count} Trade(s) · PnL {pnl:N2} USDC · Winrate {winrate:N1} %";
        }

        // ===== Account-Einstellungen =====

        private void LoadAccountList()
        {
            var cb = this.FindControl<ComboBox>("cbSettingsAccount")!;
            var items = _state.Accounts.Values.OrderBy(a => a.AccountId)
                .Select(a => new AccountChoice(a.AccountId, $"#{a.AccountId} {a.Name}{(a.IsDemo ? " (Demo)" : "")}"))
                .ToList();
            cb.ItemsSource = items;
            if (items.Count > 0) cb.SelectedIndex = 0;
        }

        private void ShowAccountSettings()
        {
            var editor = this.FindControl<SettingsEditor>("editorAccount")!;
            if (this.FindControl<ComboBox>("cbSettingsAccount")!.SelectedItem is not AccountChoice choice)
            {
                editor.Show(null);
                return;
            }

            // Fehlt für ein Konto noch ein Eintrag, wird er hier angelegt – wie bei der Hydration.
            if (!_copyState.AccountSettings.TryGetValue(choice.Id, out var settings))
            {
                settings = new CopyTradingAccountSettings { AccountId = choice.Id };
                _copyState.AccountSettings[choice.Id] = settings;
            }
            editor.Show(settings);
        }

        private void SaveAccountSettings()
        {
            if (this.FindControl<ComboBox>("cbSettingsAccount")!.SelectedItem is not AccountChoice choice) return;
            if (!_copyState.AccountSettings.TryGetValue(choice.Id, out var settings)) return;

            _settingsRepo.Upsert(settings);
            this.FindControl<TextBlock>("lblSettingsHint")!.Text =
                $"Gespeichert für Account #{choice.Id} um {DateTime.Now:HH:mm:ss}.";
        }

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