using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using PolyTrader.App.Avalonia.ViewModels;
using PolyTrader.Core.Modularity;
using PolyTrader.Modules.Accounting.Logic;
using PolyTrader.Modules.Accounting.Models;
using PolyTrader.Modules.Accounting.Persistence;
using PolyTrader.Modules.Accounting.Services;
using PolyTraderSharp;
using PolyTraderSharp.Services;

namespace PolyTrader.App.Avalonia.Views.Modules
{
    /// <summary>
    /// Accounting: Periodenabrechnung mit KPI-Kacheln und Monatsübersicht, Ledger-Ansicht und
    /// Abruf-/Status-Tab. Layout vollständig in AccountingWindow.axaml.
    /// </summary>
    public partial class AccountingWindow : UserControl
    {
        private readonly ObservableCollection<MonthlyRow> _monthly = new();
        private readonly ObservableCollection<LedgerEntry> _ledger = new();
        private readonly ObservableCollection<IngestRun> _runs = new();

        private readonly TradingState _state = null!;
        private readonly AccountingReportService _reports = null!;
        private readonly ILedgerRepository _ledgerRepo = null!;
        private readonly IIngestRunRepository _runRepo = null!;
        private readonly AccountingIngestService _ingest = null!;
        private readonly TerminalLogger _logger = null!;

        private PeriodStatement? _statement;
        private CurrencyContext _currency = new("USDC", 1m, true, string.Empty);

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

        public AccountingWindow(IModuleUiHost host, TradingState state, AccountingReportService reports,
            ILedgerRepository ledgerRepo, IIngestRunRepository runRepo,
            AccountingIngestService ingest, TerminalLogger logger) : this()
        {
            _state = state;
            _reports = reports;
            _ledgerRepo = ledgerRepo;
            _runRepo = runRepo;
            _ingest = ingest;
            _logger = logger;

            this.FindControl<DataGrid>("gridMonthly")!.ItemsSource = _monthly;
            this.FindControl<DataGrid>("gridLedger")!.ItemsSource = _ledger;
            this.FindControl<DataGrid>("gridRuns")!.ItemsSource = _runs;

            var cbCurrency = this.FindControl<ComboBox>("cbCurrency")!;
            cbCurrency.ItemsSource = new[] { "USDC", "USD", "EUR" };
            cbCurrency.SelectedIndex = 0;

            // Vorbelegung wie bisher: laufender Monat.
            var now = DateTime.Now;
            this.FindControl<CalendarDatePicker>("dtFrom")!.SelectedDate = new DateTime(now.Year, now.Month, 1);
            this.FindControl<CalendarDatePicker>("dtTo")!.SelectedDate = now.Date;

            FillAccountCombos();

            this.FindControl<Button>("btnCompute")!.Click += (_, _) => Compute();
            this.FindControl<Button>("btnPdf")!.Click += async (_, _) => await ExportAsync(pdf: true);
            this.FindControl<Button>("btnCsv")!.Click += async (_, _) => await ExportAsync(pdf: false);
            this.FindControl<Button>("btnLedgerRefresh")!.Click += (_, _) => LoadLedger();
            this.FindControl<Button>("btnStatusRefresh")!.Click += (_, _) => LoadRuns();
            this.FindControl<Button>("btnIngestNow")!.Click += async (_, _) => await RunIngestAsync(backfill: false);
            this.FindControl<Button>("btnBackfill")!.Click += async (_, _) => await RunIngestAsync(backfill: true);

            Compute();
            LoadLedger();
            LoadRuns();
        }

        private void Status(string text) => this.FindControl<TextBlock>("lblStatus")!.Text = text;

        private void FillAccountCombos()
        {
            var items = new List<AccountChoice> { new(null, "Alle Konten") };
            items.AddRange(_state.Accounts.Values.OrderBy(a => a.AccountId)
                .Select(a => new AccountChoice(a.AccountId, $"#{a.AccountId} {a.Name}{(a.IsDemo ? " (Demo)" : "")}")));

            foreach (var name in new[] { "cbAccount", "cbLedgerAccount", "cbStatusAccount" })
            {
                var cb = this.FindControl<ComboBox>(name)!;
                cb.ItemsSource = items.ToList();
                cb.SelectedIndex = 0;
            }
        }

        private int? AccountIdOf(string comboName) =>
            (this.FindControl<ComboBox>(comboName)!.SelectedItem as AccountChoice)?.Id;

        // ===== Übersicht / BWA =====

        private void Compute()
        {
            try
            {
                DateTime from = this.FindControl<CalendarDatePicker>("dtFrom")!.SelectedDate?.Date ?? DateTime.Today;
                DateTime to = this.FindControl<CalendarDatePicker>("dtTo")!.SelectedDate?.Date ?? DateTime.Today;
                int? accountId = AccountIdOf("cbAccount");
                string code = this.FindControl<ComboBox>("cbCurrency")!.SelectedItem as string ?? "USDC";

                _currency = _reports.ResolveCurrency(code, to);
                _statement = _reports.BuildStatement(accountId, from, to);

                string unit = _currency.Code;
                decimal V(decimal usdc) => AccountingReportService.Convert(usdc, _currency);

                this.FindControl<TextBlock>("kpiNet")!.Text = $"{V(_statement.NetTradingResultUsdc):N2} {unit}";
                this.FindControl<TextBlock>("kpiClosing")!.Text = $"{V(_statement.ClosingBalanceUsdc):N2} {unit}";
                this.FindControl<TextBlock>("kpiDeposits")!.Text = $"{V(_statement.Deposits):N2} {unit}";
                this.FindControl<TextBlock>("kpiWithdrawals")!.Text = $"{V(_statement.Withdrawals):N2} {unit}";
                this.FindControl<TextBlock>("kpiFees")!.Text = $"{V(_statement.Fees):N2} {unit}";
                this.FindControl<TextBlock>("kpiRewards")!.Text = $"{V(_statement.Rewards):N2} {unit}";
                this.FindControl<TextBlock>("kpiVolume")!.Text = $"{V(_statement.TradeVolume):N2} {unit}";
                this.FindControl<TextBlock>("kpiTrades")!.Text = _statement.TradeCount.ToString();

                this.FindControl<TextBlock>("lblCurrencyNote")!.Text = _currency.Note;

                _monthly.Clear();
                foreach (var m in _reports.BuildMonthly(accountId, from, to))
                    _monthly.Add(new MonthlyRow
                    {
                        Month = m.From.ToString("MM/yyyy"),
                        Net = $"{V(m.NetTradingResultUsdc):N2}",
                        Deposits = $"{V(m.Deposits):N2}",
                        Withdrawals = $"{V(m.Withdrawals):N2}",
                        Fees = $"{V(m.Fees):N2}",
                        Volume = $"{V(m.TradeVolume):N2}",
                        Trades = m.TradeCount,
                        Closing = $"{V(m.ClosingBalanceUsdc):N2}"
                    });

                Status($"Abrechnung berechnet ({from:dd.MM.yyyy}–{to:dd.MM.yyyy}, {unit}).");
            }
            catch (Exception ex)
            {
                Status($"Berechnung fehlgeschlagen: {ex.Message}");
            }
        }

        // ===== Export =====

        private async Task ExportAsync(bool pdf)
        {
            if (_statement == null) { Status("Bitte zuerst berechnen."); return; }

            try
            {
                // Seit der Umstellung auf Seiten ist „this" kein TopLevel mehr – der Dateidialog
                // gehört dem Shell-Fenster und wird über den Baum aufgelöst.
                var storage = TopLevel.GetTopLevel(this)?.StorageProvider;
                if (storage == null) { Status("Kein Fenster für den Dateidialog verfügbar."); return; }

                var picker = await storage.SaveFilePickerAsync(new global::Avalonia.Platform.Storage.FilePickerSaveOptions
                {
                    Title = pdf ? "PDF-Export" : "CSV-Export",
                    SuggestedFileName = $"abrechnung_{_statement.From:yyyyMMdd}_{_statement.To:yyyyMMdd}" + (pdf ? ".pdf" : ".csv"),
                    DefaultExtension = pdf ? "pdf" : "csv"
                });
                if (picker == null) return;
                string path = picker.Path.LocalPath;

                int? accountId = AccountIdOf("cbAccount");
                var entries = _ledgerRepo.Query(accountId, _statement.From, _statement.To, 100000);

                if (pdf)
                {
                    byte[] bytes = PdfExporter.Render(_statement,
                        _reports.BuildMonthly(accountId, _statement.From, _statement.To),
                        entries, _currency.Code, _currency.Factor, _currency.Note);
                    await File.WriteAllBytesAsync(path, bytes);
                }
                else
                {
                    await File.WriteAllTextAsync(path, CsvExporter.Statement(_statement) + "\n" + CsvExporter.Ledger(entries));
                }

                _logger.Info($"Accounting-Export geschrieben: {path}");
                Status($"Export gespeichert: {path}");
            }
            catch (Exception ex)
            {
                Status($"Export fehlgeschlagen: {ex.Message}");
            }
        }

        // ===== Ledger =====

        private void LoadLedger()
        {
            try
            {
                _ledger.Clear();
                foreach (var e in _ledgerRepo.Query(AccountIdOf("cbLedgerAccount"), null, null, 5000))
                    _ledger.Add(e);
                Status($"{_ledger.Count} Ledger-Sätze geladen.");
            }
            catch (Exception ex)
            {
                Status($"Ledger konnte nicht geladen werden: {ex.Message}");
            }
        }

        // ===== Abruf / Status =====

        private void LoadRuns()
        {
            try
            {
                _runs.Clear();
                foreach (var r in _runRepo.GetRecent(AccountIdOf("cbStatusAccount"), 200)) _runs.Add(r);
            }
            catch (Exception ex)
            {
                Status($"Abrufhistorie konnte nicht geladen werden: {ex.Message}");
            }
        }

        private async Task RunIngestAsync(bool backfill)
        {
            var incremental = this.FindControl<Button>("btnIngestNow")!;
            var full = this.FindControl<Button>("btnBackfill")!;
            incremental.IsEnabled = full.IsEnabled = false;
            try
            {
                Status(backfill ? "Backfill läuft …" : "Inkrementeller Abruf läuft …");
                await _ingest.IngestAllAsync(backfill, CancellationToken.None);
                LoadRuns();
                LoadLedger();
                Status($"Abruf abgeschlossen um {DateTime.Now:HH:mm:ss}.");
            }
            catch (Exception ex)
            {
                Status($"Abruf fehlgeschlagen: {ex.Message}");
            }
            finally
            {
                incremental.IsEnabled = full.IsEnabled = true;
            }
        }

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