using System;
using System.Collections.Generic;
using PolyTrader.Modules.Accounting.Logic;
using PolyTrader.Modules.Accounting.Models;
using PolyTrader.Modules.Accounting.Persistence;

namespace PolyTrader.Modules.Accounting.Services
{
    /// <summary>
    /// Anzeige-Währung einer Abrechnung. USDC/USD sind immer verfügbar (USD über die dokumentierte
    /// 1:1-Annahme), EUR nur, wenn ein amtlicher Tageskurs geladen ist.
    /// </summary>
    public sealed record CurrencyContext(string Code, decimal Factor, bool Available, string Note);

    /// <summary>
    /// Report-Service (A-2): baut neutrale Periodenabrechnungen aus dem Ledger und stellt die
    /// Währungs-Umrechnung (USDC/USD/EUR) bereit. Reine Aggregation liegt im <see cref="AccountingEngine"/>,
    /// FX im <see cref="FxConverter"/> – der Service verdrahtet nur Persistenz + pure Logik.
    /// </summary>
    public class AccountingReportService
    {
        private readonly ILedgerRepository _ledger;
        private readonly IFxRateRepository _fx;

        /// <summary>USDC→USD-Faktor (dokumentierte 1:1-Annahme; Zielland ggf. über Settings umstellbar).</summary>
        public decimal UsdcToUsd { get; set; } = FxConverter.DefaultUsdcToUsd;

        public AccountingReportService(ILedgerRepository ledger, IFxRateRepository fx)
        {
            _ledger = ledger;
            _fx = fx;
        }

        public PeriodStatement BuildStatement(int? accountId, DateTime from, DateTime to) =>
            AccountingEngine.BuildStatement(_ledger.GetUpTo(accountId, to), from, to, accountId);

        public List<PeriodStatement> BuildMonthly(int? accountId, DateTime from, DateTime to) =>
            AccountingEngine.BuildMonthlyBreakdown(_ledger.GetUpTo(accountId, to), from, to, accountId);

        /// <summary>Löst die Anzeige-Währung zum Stichtag auf (für EUR wird der EZB-Kurs am/vor dem Datum genutzt).</summary>
        public CurrencyContext ResolveCurrency(string code, DateTime asOf)
        {
            switch ((code ?? "USDC").Trim().ToUpperInvariant())
            {
                case "USD":
                    return new CurrencyContext("USD", UsdcToUsd, true, $"USDC≈USD (1:1-Annahme, Faktor {UsdcToUsd}).");
                case "EUR":
                    var rate = FxConverter.NearestOnOrBefore(_fx.GetAll(), asOf);
                    if (rate == null)
                        return new CurrencyContext("EUR", 0m, false, "Keine EZB-Tageskurse geladen – EUR nicht verfügbar.");
                    return new CurrencyContext("EUR", UsdcToUsd * rate.UsdToEur, true,
                        $"USD→EUR {rate.UsdToEur} ({rate.Date:yyyy-MM-dd}, {rate.Source}); USDC≈USD 1:1.");
                default:
                    return new CurrencyContext("USDC", 1.0m, true, "Native Buchungswährung.");
            }
        }

        public static decimal Convert(decimal usdc, CurrencyContext ctx) =>
            Math.Round(usdc * ctx.Factor, 2, MidpointRounding.AwayFromZero);
    }
}
