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

namespace FruitBankHybrid.Shared.Tests.Ekaer;

/// <summary>
/// Unit tesztek a <see cref="ShippingToEkaerMapper"/>-re — a FruitBank <c>Shipping</c> → NAV EKÁER tradeCard
/// leképezésre. Tisztán memóriában felépített entitásokon fut (nincs hálózat/DB), determinisztikus.
/// </summary>
[TestClass]
public sealed class ShippingToEkaerMapperTests
{
    // EurHufRate > 0 kell: a teszt-partnernek nincs Currency-je (null → külföldiként kezelt), és árfolyam nélkül
    // a value-számítás ResolveRateToHuf-ja dobna. A value-t a tesztek nem ellenőrzik; ez csak az átváltáshoz kell.
    private static readonly ShippingToEkaerMapper Mapper = new(new EkaerSettings { EurHufRate = 356 });

    // ---- Helpers ------------------------------------------------------------

    private static Shipping CreateShipping(string sellerCountry = "HU", string plate = "ABC-123", bool withTrailer = true)
    {
        var item = new ShippingItem
        {
            Id = 1,
            Name = "Alma",
            ProductDto = new ProductDto { Gtin = "08081010", Name = "Alma" },
            MeasuredGrossWeight = 123.5,
            MeasuredQuantity = 10,
            UnitPriceOnDocument = 5.0,
        };

        var document = new ShippingDocument
        {
            Country = sellerCountry,
            Partner = new Partner
            {
                Name = "Beszállító Kft",
                TaxId = "12345678-2-42",
                CountryCode = sellerCountry,
                PostalCode = "1011",
                City = "Budapest",
                Street = "Fő utca 1",
            },
            ShippingItems = [item],
        };

        var shipping = new Shipping
        {
            CargoPartner = new CargoPartner { Name = "Fuvaros Zrt", CountryCode = "HU" },
            CargoTruck = new CargoTruck { LicencePlate = plate, CountryCode = "HU", IsTrailer = false },
            ShippingDocuments = [document],
        };

        if (withTrailer)
            shipping.CargoTrailer = new CargoTruck { LicencePlate = "XYZ-789", CountryCode = "HU", IsTrailer = true };

        return shipping;
    }

    private static EkaerCompanyInfo CreateCompany() => new()
    {
        Name = "FruitBank Kft",
        TaxId = "98765432-2-41",
        CountryCode = "HU",
        PostalCode = "1102",
        City = "Budapest",
        Street = "Raktar utca 5",
        Site = new LocationType
        {
            Name = "FruitBank Raktár",
            VatNumber = "98765432-2-41",
            Phone = "+36301234567",          // NAV-formátum: + / 06 prefix kötelező
            Email = "raktar@fruitbank.hu",
            Country = "HU",
            ZipCode = "1239",
            City = "Budapest",
            Street = "Nagykőrösi út",
            StreetNumber = "353",
        },
    };

    private static ShippingDocument CreateInboundDocument(int itemId, double weight, string sellerCountry = "HU") => new()
    {
        Country = sellerCountry,
        Partner = new Partner
        {
            Name = "Beszállító Kft",
            TaxId = "12345678-2-42",
            CountryCode = sellerCountry,
            PostalCode = "1011",
            City = "Budapest",
            Street = "Fő utca 1",
        },
        ShippingItems = [new ShippingItem
        {
            Id = itemId,
            Name = "Alma",
            ProductDto = new ProductDto { Gtin = "08081010", Name = "Alma" },
            MeasuredGrossWeight = weight,
            UnitPriceOnDocument = 5.0,
        }],
    };

    // ---- Granularitás / index ----------------------------------------------

    [TestMethod]
    public void MapShipping_ProducesOneOperationPerDocument_WithSequentialIndex()
    {
        var shipping = CreateShipping();
        shipping.ShippingDocuments!.Add(new ShippingDocument
        {
            Country = "HU",
            Partner = new Partner { Name = "Másik Beszállító", CountryCode = "HU" },
            ShippingItems = [],
        });

        var ops = Mapper.MapShipping(shipping, CreateCompany());

        Assert.AreEqual(2, ops.Count, "dokumentumonként egy tradeCard");
        Assert.AreEqual(1, ops[0].Index);
        Assert.AreEqual(2, ops[1].Index);
        Assert.AreEqual(OperationType.Create, ops[0].Operation, "alapértelmezett művelet: Create");
    }

    [TestMethod]
    public void MapShipping_NullDocuments_ReturnsEmpty()
    {
        var ops = Mapper.MapShipping(new Shipping { ShippingDocuments = null }, CreateCompany());
        Assert.AreEqual(0, ops.Count);
    }

    [TestMethod]
    public void MapShipping_HonorsExplicitOperation()
    {
        var ops = Mapper.MapShipping(CreateShipping(), CreateCompany(), OperationType.Modify);
        Assert.AreEqual(OperationType.Modify, ops[0].Operation);
    }

    // ---- tradeType irány ----------------------------------------------------

    [TestMethod]
    public void MapShipping_DomesticSeller_TradeTypeDomestic()
    {
        var ops = Mapper.MapShipping(CreateShipping(sellerCountry: "HU"), CreateCompany());
        Assert.AreEqual(TradeType.D, ops[0].TradeCard.TradeType, "belföldi (HU) feladó → D");
    }

    [TestMethod]
    public void MapShipping_ForeignSeller_TradeTypeImport()
    {
        var ops = Mapper.MapShipping(CreateShipping(sellerCountry: "DE"), CreateCompany());
        Assert.AreEqual(TradeType.I, ops[0].TradeCard.TradeType, "nem-HU feladó → I (import) — a NAV EKÁER magyar");
    }

    // ---- Tétel-leképezés ----------------------------------------------------

    [TestMethod]
    public void MapShipping_MapsItemFields()
    {
        var item = Mapper.MapShipping(CreateShipping(), CreateCompany())[0].TradeCard.Items[0];

        Assert.AreEqual("08081010", item.ProductVtsz, "productVtsz = ProductDto.Gtin");
        Assert.AreEqual("Alma", item.ProductName);
        Assert.AreEqual(123.5m, item.Weight, "weight = MeasuredGrossWeight (bruttó)");
        Assert.AreEqual(TradeReasonType.A, item.TradeReason, "bejövő áru = beszerzés = A");
        Assert.IsNull(item.Value, "a HUF érték a deviza tisztázásáig nincs kitöltve");
    }

    // ---- Eladó (seller*) ----------------------------------------------------

    [TestMethod]
    public void MapShipping_MapsSellerFromPartner()
    {
        var tradeCard = Mapper.MapShipping(CreateShipping(), CreateCompany())[0].TradeCard;

        Assert.AreEqual("Beszállító Kft", tradeCard.SellerName);
        Assert.AreEqual("12345678-2-42", tradeCard.SellerVatNumber);
        Assert.AreEqual("HU", tradeCard.SellerCountry);
        StringAssert.Contains(tradeCard.SellerAddress, "Budapest", "a sellerAddress a Partner FullAddress-éből jön");
    }

    [TestMethod]
    public void MapShipping_MapsDestinationAndUnloadFromCompany()
    {
        var company = CreateCompany();
        var tradeCard = Mapper.MapShipping(CreateShipping(), company)[0].TradeCard;

        Assert.AreEqual("FruitBank Kft", tradeCard.DestinationName);
        Assert.AreEqual("HU", tradeCard.DestinationCountry);
        StringAssert.Contains(tradeCard.DestinationAddress, "Budapest", "a destinationAddress a company FullAddress-éből jön");
        Assert.AreSame(company.Site, tradeCard.UnloadLocation, "a lerakodási hely a cég telephelyéből jön");
    }

    [TestMethod]
    public void MapShipping_MapsCarrierTextFromCargoPartner()
    {
        var tradeCard = Mapper.MapShipping(CreateShipping(), CreateCompany())[0].TradeCard;
        Assert.AreEqual("Fuvaros Zrt", tradeCard.CarrierText);
    }

    // ---- Járművek + rendszám-normalizálás ----------------------------------

    [TestMethod]
    public void MapShipping_NormalizesLicencePlate_RemovesHyphenAndUppercases()
    {
        var tradeCard = Mapper.MapShipping(CreateShipping(plate: "abc-123"), CreateCompany())[0].TradeCard;
        Assert.AreEqual("ABC123", tradeCard.Vehicle!.PlateNumber, "a NAV pattern nem enged kötőjelet, és nagybetűs");
        Assert.AreEqual("HU", tradeCard.Vehicle.Country);
    }

    [TestMethod]
    public void MapShipping_WithTrailer_MapsVehicle2()
    {
        var tradeCard = Mapper.MapShipping(CreateShipping(withTrailer: true), CreateCompany())[0].TradeCard;
        Assert.IsNotNull(tradeCard.Vehicle, "vonó jármű");
        Assert.IsNotNull(tradeCard.Vehicle2, "vontatmány");
        Assert.AreEqual("XYZ789", tradeCard.Vehicle2!.PlateNumber);
    }

    [TestMethod]
    public void MapShipping_NoTrailer_Vehicle2Null()
    {
        var tradeCard = Mapper.MapShipping(CreateShipping(withTrailer: false), CreateCompany())[0].TradeCard;
        Assert.IsNotNull(tradeCard.Vehicle);
        Assert.IsNull(tradeCard.Vehicle2, "nincs pótkocsi → nincs vehicle2");
    }

    // ---- Védőkorlátok -------------------------------------------------------

    [TestMethod]
    public void MapShipping_NullShipping_Throws()
        => Assert.ThrowsExactly<ArgumentNullException>(() => Mapper.MapShipping(null!, CreateCompany()));

    [TestMethod]
    public void MapShipping_NullCompany_Throws()
        => Assert.ThrowsExactly<ArgumentNullException>(() => Mapper.MapShipping(CreateShipping(), null!));

    // ---- Köztes modell: ToConsignment (bejövő) ------------------------------

    [TestMethod]
    public void ToConsignment_Inbound_MultipleDocuments_CombinesLinesAndAggregatesWeight()
    {
        // Két szállítólevél (azonos partner, azonos Shipping) → egy szállítmány, MINDKÉT tétellel — a küszöb-aggregáláshoz.
        var consignment = Mapper.ToConsignment(
            [CreateInboundDocument(itemId: 1, weight: 100), CreateInboundDocument(itemId: 2, weight: 150)],
            CreateCompany());

        Assert.AreEqual(2, consignment.Lines.Count, "a csoport ÖSSZES tétele egy szállítmányban");
        Assert.AreEqual(250d, consignment.Lines.Sum(l => l.WeightKg), 0.001, "a tömeg a dokumentumokból összegződik");
        Assert.IsFalse(consignment.IsOutgoing);
        Assert.AreEqual("HU", consignment.Seller.CountryCode, "feladó = a beszállító partner");
        Assert.AreEqual("HU", consignment.Buyer.CountryCode, "címzett = a saját cég");
    }

    [TestMethod]
    public void ToConsignment_Inbound_BuildTradeCard_PreservesCompanySiteAsUnloadLocation()
    {
        var company = CreateCompany();
        var tradeCard = Mapper.BuildTradeCard(Mapper.ToConsignment([CreateInboundDocument(1, 100)], company));
        Assert.AreSame(company.Site, tradeCard.UnloadLocation, "a saját telephely érintetlenül (mezővesztés nélkül) megy át");
    }

    [TestMethod]
    public void ToConsignment_Inbound_EmptyDocuments_Throws()
        => Assert.ThrowsExactly<ArgumentException>(() => Mapper.ToConsignment([], CreateCompany()));

    [TestMethod]
    public void ToConsignment_Inbound_NullPartner_DoesNotThrow_SellerCountryNull()
    {
        // Hibás adat: nincs Partner a szállítólevélen → az adapter NEM dob; a feladó-ország null (ezt a kötelezettség-
        // értékelő / generate-validálás kezeli, nem a leképező).
        var doc = new ShippingDocument { Partner = null, ShippingItems = [new ShippingItem { Id = 1, MeasuredGrossWeight = 50 }] };
        var consignment = Mapper.ToConsignment([doc], CreateCompany());

        Assert.IsNull(consignment.Seller.CountryCode, "nincs partner → nincs feladó-ország");
        Assert.AreEqual(1, consignment.Lines.Count);
    }

    [TestMethod]
    public void ToConsignment_Inbound_NullShippingItems_EmptyLines()
    {
        // Hibás/hiányos adat: nincs tétel a szállítólevélen → üres Lines, NEM dob.
        var doc = new ShippingDocument { Partner = new Partner { CountryCode = "HU" }, ShippingItems = null };
        var consignment = Mapper.ToConsignment([doc], CreateCompany());

        Assert.AreEqual(0, consignment.Lines.Count);
    }
}
