using AyCode.Services.Nav;
using AyCode.Services.Nav.Ekaer;
using AyCode.Services.Nav.Ekaer.Models;
using FruitBank.Common.Dtos;
using FruitBank.Common.Entities;
using FruitBank.Common.Services.Ekaer;

namespace FruitBank.Common.Server.Services.Ekaer;

/// <inheritdoc cref="IFruitBankEkaerService"/>
/// <remarks>
/// A teljes lánc: <c>map</c> (<see cref="IShippingToEkaerMapper"/>, FruitBank.Common) →
/// <c>validate → send</c> (<see cref="IEkaerSubmitService"/>, AyCode.Services). A saját cégadatot
/// (<see cref="EkaerSettings.Company"/>) és a NAV-fiók hitelesítő adatait a DI szolgáltatja.
/// </remarks>
public sealed class FruitBankEkaerService : IFruitBankEkaerService
{
    private readonly IShippingToEkaerMapper _mapper;
    private readonly IEkaerSubmitService _submitService;
    private readonly IEkaerTradeCardValidator _validator;
    private readonly IEkaerSettings _settings;

    public FruitBankEkaerService(IShippingToEkaerMapper mapper, IEkaerSubmitService submitService, IEkaerTradeCardValidator validator, IEkaerSettings settings)
    {
        _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
        _submitService = submitService ?? throw new ArgumentNullException(nameof(submitService));
        _validator = validator ?? throw new ArgumentNullException(nameof(validator));
        _settings = settings ?? throw new ArgumentNullException(nameof(settings));
    }

    public Task<EkaerSubmitResult> SubmitShippingAsync(Shipping shipping, OperationType operation = OperationType.Create, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(shipping);

        // map (FruitBank.Common) → submit: validate → send (AyCode.Services)
        var operations = _mapper.MapShipping(shipping, _settings.Company, operation);
        return _submitService.SubmitAsync(operations, cancellationToken);
    }

    public EkaerHistory GenerateEkaerXmlDocument(IReadOnlyCollection<ShippingDocument> documents, EkaerHistory? ekaerHistory = null)
    {
        ArgumentNullException.ThrowIfNull(documents);
        if (documents.Count == 0) throw new ArgumentException("documents is empty", nameof(documents));
        ekaerHistory ??= new EkaerHistory { IsOutgoing = false };

        // A csoport azonos Partneré (a kapu így csoportosít) → az első dokumentum pénzneme a mérvadó.
        var currency = documents.First().Partner?.Currency;
        // Guardok a leképezés ELŐTT (nincs félrevezető XML), majd a csoport ÖSSZES dokumentuma EGY tradeCard-dá
        // (összevont tömeg/érték): ToConsignment → BuildTradeCard.
        return TryDepotError(ekaerHistory, documents)
            ?? TryConfigError(ekaerHistory, currency)
            ?? Finalize(ekaerHistory, _mapper.BuildTradeCard(_mapper.ToConsignment(documents, _settings.Company)), currency);
    }

    public EkaerHistory GenerateEkaerXmlDocument(OrderDto order, EkaerHistory? ekaerHistory = null)
    {
        ArgumentNullException.ThrowIfNull(order);
        ekaerHistory ??= new EkaerHistory { IsOutgoing = true };

        // Kimenő pénznem: jelenleg minden HUF (a deviza az OrderDto-ba kerül, amint bekötik) → ConversionRate = 1.
        const string currency = "HUF";
        return TryConfigError(ekaerHistory, currency) ?? Finalize(ekaerHistory, _mapper.MapOrder(order, _settings.Company), currency);
    }

    public EkaerObligationResult EvaluateObligation(IReadOnlyCollection<ShippingDocument> documents)
        => EkaerReportability.Evaluate(_mapper.ToConsignment(documents, _settings.Company), _settings);

    public EkaerObligationResult EvaluateObligation(OrderDto order)
        => EkaerReportability.Evaluate(_mapper.ToConsignment(order, _settings.Company), _settings);

    public void SetSummary(EkaerHistory history, DateTime? shippingDate, string? partner)
    {
        history.ShippingDate = shippingDate;
        history.Partner = partner;
    }

    /// <summary>Depot-kapu (T-D8R4): a PartnerDepot az EKÁER-hez kötelező — a felrakodási hely a depot címe. Hiányzó
    /// vagy be nem töltött telephelynél a leképezés ELŐTT ValidationError (nincs félrevezető, partner-központi-címes XML).
    /// <c>null</c> = rendben, mehet a generálás.</summary>
    private static EkaerHistory? TryDepotError(EkaerHistory ekaerHistory, IReadOnlyCollection<ShippingDocument> documents)
    {
        if (documents.All(d => d.PartnerDepot != null)) return null;

        ekaerHistory.Status = EkaerStatus.ValidationError;
        ekaerHistory.ErrorText = "A szállítólevélhez nincs telephely (PartnerDepot) rendelve — EKÁER-hez kötelező (a felrakodási hely a telephely címe); pótlás után generáld újra.";
        return ekaerHistory;
    }

    /// <summary>Config-kapu: külföldi (nem HUF) feladónál az árfolyam kötelező — különben a leképezés ELŐTT
    /// ValidationError (nincs félrevezető XML). <c>null</c> = rendben, mehet a generálás.</summary>
    private EkaerHistory? TryConfigError(EkaerHistory ekaerHistory, string? currency)
    {
        if (EkaerValueCalculator.IsHuf(currency) || _settings.EurHufRate > 0) return null;

        ekaerHistory.Status = EkaerStatus.ValidationError;
        ekaerHistory.ErrorText = "EKÁER EUR-HUF árfolyam nincs konfigurálva (appsettings Ekaer:ExchangeRate:EurHuf) — a tétel-érték nem számolható.";
        return ekaerHistory;
    }

    /// <summary>Közös befejezés (bejövő/kimenő): validál, szerializál; az XML validációs hibánál IS tárolódik
    /// (a detail-nézethez), és rögzíti a ténylegesen alkalmazott árfolyamot (HUF → 1, külföldi → FX-ráta).</summary>
    private EkaerHistory Finalize(EkaerHistory ekaerHistory, TradeCardType tradeCard, string? currency)
    {
        var operation = new TradeCardOperationType { Index = 1, Operation = OperationType.Create, TradeCard = tradeCard };
        var messages = _validator.Validate(operation);

        ekaerHistory.XmlDoc = NavXmlHelper.Serialize(tradeCard);
        ekaerHistory.ConversionRate = EkaerValueCalculator.ResolveRateToHuf(currency, _settings.EurHufRate);
        // Blokkoló hiba → ValidationError (nem küldhető); csak warning → GeneratedWithWarning (küldhető, de pótlandó);
        // semmi → Generated. Az üzeneteket súlyossággal prefixeljük, hogy a detail-nézet megkülönböztesse őket.
        ekaerHistory.Status = messages.HasErrors() ? EkaerStatus.ValidationError
            : messages.HasWarnings() ? EkaerStatus.GeneratedWithWarning
            : EkaerStatus.Generated;
        // Error-ok elöl, warningok hátul (a detail-nézet soronként színez); a prefix [Error]/[Warning] hordozza a szintet.
        ekaerHistory.ErrorText = messages.Count == 0 ? null
            : string.Join(Environment.NewLine, messages
                .OrderByDescending(m => m.GetSeverity())
                .Select(m => $"[{m.GetSeverity()}] {m.ErrorMessage}"));
        return ekaerHistory;
    }
}
