using AyCode.Services.Nav.Ekaer;

namespace FruitBank.Common.Services.Ekaer;

/// <summary>Egy szállítmány EKÁER bejelentés-kötelezettsége.</summary>
public enum EkaerObligation
{
    /// <summary>Kötelező bejelenteni (sort kell létrehozni).</summary>
    Required,
    /// <summary>Nem kötelező (belföld, küszöb alatt) — NEM hiba, nincs üzenet.</summary>
    NotRequired,
    /// <summary>A döntés nem hozható meg adathiány/formátumhiba miatt (pl. érvénytelen országkód) — NINCS sor, az okok az üzenetekben.</summary>
    DataError,
}

/// <summary>A kötelezettség-kiértékelés eredménye: a döntés + (csak DataError esetén) a felhasználónak szóló okok.</summary>
public sealed class EkaerObligationResult
{
    public EkaerObligation Obligation { get; private init; }
    public IReadOnlyList<string> Errors { get; private init; } = [];

    public static EkaerObligationResult Required { get; } = new() { Obligation = EkaerObligation.Required };
    public static EkaerObligationResult NotRequired { get; } = new() { Obligation = EkaerObligation.NotRequired };
    public static EkaerObligationResult DataError(IReadOnlyList<string> errors) =>
        new() { Obligation = EkaerObligation.DataError, Errors = errors };
}

/// <summary>
/// EKÁER bejelentés-kötelezettség eldöntése egy normalizált <see cref="EkaerConsignment"/>-re — KÖZÖS a bejövő
/// (document-csoport) és a kimenő (order) ágon. A NAV mindkét irányban büntet (a felesleges bejelentés ÉS a kimaradás
/// is bírság), ezért: a bizonytalan adatot (érvénytelen országkód) NEM döntjük el magától → <see cref="EkaerObligation.DataError"/>.
/// </summary>
/// <remarks>
/// Sorrend: (0) TÉTELENKÉNTI előfeltétel — bruttó súly > 0, különben DataError (a 0 súly csendben alul-számolná a
/// küszöb-summát → alul-jelentés); (1) az AGGREGÁLT tömeg/érték küszöb FELETT → Required, az országkódtól FÜGGETLENÜL
/// — belföld+küszöb felett és külföld egyaránt kötelező; az országkód-hibát ilyenkor a generate-validálás jelzi, a sor
/// létrejön; (2) küszöb ALATT → itt számít az ország: hiányzó/érvénytelen országkód → DataError (a foreign-vs-belföld
/// nem dönthető el); (3) küszöb alatt, érvényes országkódok: külföld (eltér) → Required, belföld (egyezik) → NotRequired.
/// A 13/2020. (XII. 23.) PM rendelet alapján a küszöb feladó→címzett→jármű relációra aggregált — a hívó már
/// így állítja össze a <see cref="EkaerConsignment.Lines"/>-t (bejövőnél a (Shipping, Partner) csoport tételei).
/// </remarks>
public static class EkaerReportability
{
    public static EkaerObligationResult Evaluate(EkaerConsignment consignment, IEkaerSettings settings)
    {
        ArgumentNullException.ThrowIfNull(consignment);
        ArgumentNullException.ThrowIfNull(settings);

        var subject = Subject(consignment);

        // (0) Tételenkénti előfeltétel: bruttó súly > 0. A 0 súlyú tétel (kitöltetlen dokumentum-súly / méretlen
        // rendelés) csendben alul-számolná a küszöb-aggregátumot → alul-jelentési kockázat; a NAV amúgy is tételenként
        // pozitív bruttó súlyt vár. Hiányzó súly → DataError (a sor nem jön létre; pótlás után a következő futás felveszi).
        var weightErrors = consignment.Lines
            .Where(l => l.WeightKg <= 0)
            .Select(l => $"{subject} / {(string.IsNullOrWhiteSpace(l.Name) ? $"tétel #{l.ExternalId}" : l.Name)}: a tétel bruttó súlya hiányzik (0 kg) — a kötelezettség nem dönthető el.")
            .ToList();
        if (weightErrors.Count > 0)
            return EkaerObligationResult.DataError(weightErrors);

        // (1) Küszöb FELETT → kötelező, FÜGGETLENÜL az országkódtól: belföld+küszöb felett ÉS külföld egyaránt kötelező.
        // Az országkód-hibát ilyenkor NEM itt blokkoljuk — a sor létrejön, a hibát a generate-validálás jelzi.
        var totalWeight = consignment.Lines.Sum(l => l.WeightKg);
        var totalValueHuf = consignment.Lines.Sum(l => l.ValueHuf ?? 0L);
        if (totalWeight >= settings.ThresholdWeightKg || totalValueHuf >= settings.ThresholdValueHuf)
            return EkaerObligationResult.Required;

        // (2) Küszöb ALATT: itt MÁR az ország dönt — csak a külföldi (eltérő országkód) reláció kötelező. Ehhez érvényes
        // ISO-2 országkódok kellenek; hiányzó/érvénytelen → a foreign-vs-belföld nem dönthető el → DataError.
        var errors = new List<string>();
        if (!IsValidCountry(consignment.Seller.CountryCode))
            errors.Add($"{subject}: a feladó országkódja hiányzik vagy érvénytelen ('{consignment.Seller.CountryCode}').");
        if (!IsValidCountry(consignment.Buyer.CountryCode))
            errors.Add($"{subject}: a címzett országkódja hiányzik vagy érvénytelen ('{consignment.Buyer.CountryCode}').");
        if (errors.Count > 0)
            return EkaerObligationResult.DataError(errors);

        // (3) Küszöb alatt, érvényes országkódok: külföld (eltér) → kötelező, belföld (egyezik) → nem kötelező (üzenet nélkül).
        return CountryEquals(consignment.Seller.CountryCode, consignment.Buyer.CountryCode)
            ? EkaerObligationResult.NotRequired
            : EkaerObligationResult.Required;
    }

    /// <summary>Érvényes EKÁER országkód: pontosan 2 ASCII betű (ISO-2). Üres / más hosszúságú → érvénytelen.</summary>
    private static bool IsValidCountry(string? code)
    {
        var c = code?.Trim();
        return c is { Length: 2 } && c.All(char.IsAsciiLetter);
    }

    private static bool CountryEquals(string? a, string? b) => string.Equals(a?.Trim(), b?.Trim(), StringComparison.OrdinalIgnoreCase);

    private static string Subject(EkaerConsignment c)
        => c.IsOutgoing ? $"Rendelés #{c.ForeignKey}" : string.IsNullOrWhiteSpace(c.Seller.Name) ? $"Szállítólevél #{c.ForeignKey}" : c.Seller.Name!;
}
