using AyCode.Core.Interfaces;
using AyCode.Entities;
using AyCode.Services.Nav.Ekaer;
using AyCode.Services.Nav.Ekaer.Models;
using FruitBank.Common.Entities;
using TradeReasonType = AyCode.Services.Nav.Ekaer.Models.Common.TradeReasonType;
using TradeType = AyCode.Services.Nav.Ekaer.Models.Common.TradeType;

namespace FruitBank.Common.Services.Ekaer;

/// <inheritdoc cref="IShippingToEkaerMapper"/>
/// <remarks>
/// Tiszta (állapotmentes) leképező. A feladót és a saját céget egységesen <see cref="ICompanyInfoBase"/>-ként kezeli.
/// A <c>TradeType</c>/<c>TradeReasonType</c> enumokat aliasszal hozzuk be a <c>Models.TradeCardType</c> osztály
/// és a <c>Models.Common.TradeCardType</c> enum névütközése miatt.
/// </remarks>
public sealed class ShippingToEkaerMapper : IShippingToEkaerMapper
{
    /// <summary>A NAV EKÁER magyar rendszer — a „belföld" mindig HU; minden más feladó-ország import.</summary>
    private const string HomeCountry = "HU";

    public IReadOnlyList<TradeCardOperationType> MapShipping(Shipping shipping, EkaerCompanyInfo company, OperationType operation = OperationType.Create)
    {
        ArgumentNullException.ThrowIfNull(shipping);
        ArgumentNullException.ThrowIfNull(company);

        var operations = new List<TradeCardOperationType>();
        var index = 0;

        // Granularitás: egy ShippingDocument → egy tradeCard (lásd EKAER_TODO #5).
        foreach (var document in shipping.ShippingDocuments ?? [])
        {
            index++;
            operations.Add(new TradeCardOperationType
            {
                Index = index,
                Operation = operation,
                TradeCard = BuildTradeCard(shipping, document, company),
            });
        }

        return operations;
    }

    private static TradeCardType BuildTradeCard(Shipping shipping, ShippingDocument document, EkaerCompanyInfo company)
    {
        var seller = document.Partner; // a beszállító (feladó) — ICompanyInfoBase

        var tradeCard = new TradeCardType
        {
            TradeType = ResolveTradeType(seller),
            ModByCarrierEnabled = false, // mi jelentünk; a fuvarozó alapból nem módosíthat

            // Feladó / eladó = a beszállító
            SellerName = seller?.Name,
            SellerVatNumber = NormalizeVatNumber(seller?.TaxId),
            SellerCountry = NormalizeCountryCode(seller?.CountryCode, 2),
            SellerAddress = Truncate(seller?.FullAddress, 200),

            // Címzett = a bejelentő saját cége (bejövő relációban)
            DestinationName = company.Name,
            DestinationVatNumber = NormalizeVatNumber(company.TaxId),
            DestinationCountry = NormalizeCountryCode(company.CountryCode, 2),
            DestinationAddress = Truncate(company.FullAddress, 200),

            // Fuvarozó (Shipping.CargoPartner). Regisztrált EKAER-azonosító nincs, csak szöveges név.
            CarrierText = shipping.CargoPartner?.Name,

            // Lerakodás = saját telephely (a cégadatból); felrakodás = a beszállító telephelye.
            UnloadLocation = company.UnloadLocation,
            LoadLocation = BuildLoadLocation(seller),
        };

        // Vonó jármű + vontatmány: az EKÁER külön bejegyzésként kéri (vehicle / vehicle2).
        if (shipping.CargoTruck != null) tradeCard.Vehicle = BuildVehicle(shipping.CargoTruck);
        if (shipping.CargoTrailer != null) tradeCard.Vehicle2 = BuildVehicle(shipping.CargoTrailer);

        foreach (var item in document.ShippingItems ?? []) tradeCard.Items.Add(BuildItem(item));
        return tradeCard;
    }

    /// <summary>
    /// Belföldi feladó (HU) → <c>D</c> (belföld-belföld), egyébként → <c>I</c> (import). A NAV EKÁER magyar,
    /// így a belföld mindig HU; az export (<c>E</c>) jelenleg nincs leképezve (lásd EKAER_TODO #1, #7).
    /// </summary>
    private static TradeType ResolveTradeType(ICompanyInfoBase? seller)
        => string.Equals(seller?.CountryCode, HomeCountry, StringComparison.OrdinalIgnoreCase)
            ? TradeType.D
            : TradeType.I;

    private static TradeCardItemType BuildItem(ShippingItem item) => new()
    {
        ItemExternalId = item.Id.ToString(),
        // Bejövő áru = beszerzés → A. (Enum: S=értékesítés, A=beszerzés, W=bérmunka, O=egyéb.) Lásd EKAER_TODO #9.
        TradeReason = TradeReasonType.A,
        ProductVtsz = item.ProductDto?.Gtin, // VTSZ — átmenetileg a Gtin oszlopban (FBANKAPP-DMODEL-I-P6X4)
        ProductName = item.ProductName,
        Weight = (decimal)item.MeasuredGrossWeight, // bruttó tömeg kg-ban (lásd EKAER_TODO #4)
        // Value (HUF): a deviza/FX tisztázásáig NEM töltjük (a mező opcionális). Lásd EKAER_TODO #3, #10.
    };

    private static BasicVehicleDetailType BuildVehicle(CargoTruck truck) => new()
    {
        PlateNumber = NormalizePlateNumber(truck.LicencePlate),
        Country = NormalizeCountryCode(truck.CountryCode, 3),
    };

    /// <summary>
    /// Felrakodási hely a beszállító adataiból. (Magyar feladónál a NAV a Phone/Email-t is kéri, ami az
    /// entitásban nincs — lásd EKAER_TODO #6.)
    /// </summary>
    private static LocationType? BuildLoadLocation(ICompanyInfoBase? seller)
    {
        if (seller is null) return null;
        return new LocationType
        {
            Name = seller.Name,
            VatNumber = NormalizeVatNumber(seller.TaxId),
            Country = NormalizeCountryCode(seller.CountryCode, 2),
            ZipCode = NormalizeZipCode(seller.PostalCode),
            City = seller.City,
            Street = seller.Street,
        };
    }

    /// <summary>Adószám normalizálása. Pattern: <c>[0-9A-Z-]{1,15}</c>.</summary>
    private static string? NormalizeVatNumber(string? value)
    {
        if (string.IsNullOrWhiteSpace(value)) return null;
        var cleaned = new string([.. value.ToUpperInvariant().Where(c => char.IsAsciiLetterOrDigit(c) || c == '-')]);
        return Truncate(EmptyToNull(cleaned), 15);
    }

    /// <summary>Országkód normalizálása. Pattern: <c>[A-Z]{1,maxLen}</c> (seller/location: 2, jármű: 3).</summary>
    private static string? NormalizeCountryCode(string? value, int maxLen)
    {
        if (string.IsNullOrWhiteSpace(value)) return null;
        var cleaned = new string([.. value.ToUpperInvariant().Where(char.IsAsciiLetter)]);
        return Truncate(EmptyToNull(cleaned), maxLen);
    }

    /// <summary>Rendszám normalizálása. Pattern: <c>[A-Z0-9ÖŐÜŰ]{4,15}</c> — kötőjel/szóköz NEM engedett.</summary>
    private static string? NormalizePlateNumber(string? value)
    {
        if (string.IsNullOrWhiteSpace(value)) return null;
        var cleaned = new string([.. value.ToUpperInvariant().Where(c => char.IsAsciiLetterOrDigit(c) || c is 'Ö' or 'Ő' or 'Ü' or 'Ű')]);
        return Truncate(EmptyToNull(cleaned), 15);
    }

    /// <summary>Irányítószám normalizálása. Pattern: <c>[A-Z0-9 -]{2,10}</c> vagy üres.</summary>
    private static string? NormalizeZipCode(string? value)
    {
        if (string.IsNullOrWhiteSpace(value)) return null;
        var cleaned = new string([.. value.ToUpperInvariant().Where(c => char.IsAsciiLetterOrDigit(c) || c is ' ' or '-')]);
        return Truncate(EmptyToNull(cleaned), 10);
    }

    private static string? EmptyToNull(string? value) => string.IsNullOrEmpty(value) ? null : value;

    private static string? Truncate(string? value, int maxLen)
        => value is null ? null : value.Length <= maxLen ? value : value[..maxLen];
}
