using System.ComponentModel.DataAnnotations;
using AyCode.Services.Nav.Ekaer;
using AyCode.Services.Nav.Ekaer.Models;
using TradeReasonType = AyCode.Services.Nav.Ekaer.Models.Common.TradeReasonType;
using TradeType = AyCode.Services.Nav.Ekaer.Models.Common.TradeType;

namespace AyCode.Services.Tests.Nav;

/// <summary>
/// Unit tesztek az <see cref="EkaerTradeCardValidator"/>-ra: séma-szintű (generált DataAnnotations) és
/// üzleti szabályok (vonó jármű, tétel, feladó/címzett) ellenőrzése, hibalista-jelleggel. A validátor
/// általános NAV/EKÁER réteg — a teszt csak az AyCode.Services-re támaszkodik (semmi projekt-specifikus).
/// </summary>
[TestClass]
public sealed class EkaerTradeCardValidatorTests
{
    private static readonly EkaerTradeCardValidator Sut = new();

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

    /// <summary>Teljesen érvényes Create művelet, egy érvényes tétellel. A tesztek ezt „rontják el".</summary>
    private static TradeCardOperationType ValidOperation()
    {
        var operation = new TradeCardOperationType
        {
            Index = 1,
            Operation = OperationType.Create,
            TradeCard = new TradeCardType
            {
                TradeType = TradeType.I,
                SellerName = "Beszállító Kft",
                SellerVatNumber = "12345678-2-42",
                SellerCountry = "HU",
                SellerAddress = "1011 Budapest Fo utca 1",
                DestinationName = "FruitBank Kft",
                DestinationVatNumber = "98765432-2-41",
                DestinationCountry = "HU",
                DestinationAddress = "1102 Budapest Raktar utca 5",
                Vehicle = new BasicVehicleDetailType { PlateNumber = "ABC123", Country = "HU" },
            },
        };
        operation.TradeCard.Items.Add(new TradeCardItemType
        {
            ItemExternalId = "1",
            TradeReason = TradeReasonType.A,
            ProductVtsz = "08081010",
            ProductName = "Alma",
            Weight = 123.5m,
            Value = 250_000,
        });
        return operation;
    }

    private static bool HasError(IReadOnlyList<ValidationResult> errors, string fragment)
        => errors.Any(e => e.ErrorMessage?.Contains(fragment, StringComparison.OrdinalIgnoreCase) == true);

    // ---- Érvényes eset ------------------------------------------------------

    [TestMethod]
    public void Validate_ValidOperation_NoErrors()
    {
        var errors = Sut.Validate(ValidOperation());
        Assert.AreEqual(0, errors.Count, $"érvényes művelet nem adhat hibát; kapott: {string.Join(" | ", errors.Select(e => e.ErrorMessage))}");
    }

    // ---- Üzleti szabályok ---------------------------------------------------

    [TestMethod]
    public void Validate_MissingVehicle_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Vehicle = null;
        Assert.IsTrue(HasError(Sut.Validate(op), "vehicle"), "a hiányzó vonó jármű hibát ad");
    }

    [TestMethod]
    public void Validate_NoItems_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Items.Clear();
        Assert.IsTrue(HasError(Sut.Validate(op), "tétel"), "a tétel nélküli tradeCard hibát ad");
    }

    [TestMethod]
    public void Validate_MissingSellerName_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.SellerName = null;
        Assert.IsTrue(HasError(Sut.Validate(op), "feladó"), "hiányzó feladónév → hiba");
    }

    [TestMethod]
    public void Validate_MissingDestinationName_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.DestinationName = "   ";
        Assert.IsTrue(HasError(Sut.Validate(op), "címzett"), "üres/whitespace címzettnév → hiba");
    }

    [TestMethod]
    public void Validate_ZeroWeight_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Items[0].Weight = 0;
        Assert.IsTrue(HasError(Sut.Validate(op), "tömege"), "0 tömeg (hiányzó súly a doksin) → üzleti szabály hiba");
    }

    [TestMethod]
    public void Validate_MissingValue_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Items[0].Value = null;
        Assert.IsTrue(HasError(Sut.Validate(op), "értéke"), "hiányzó tétel-érték → üzleti szabály hiba (NAV 2021-től)");
    }

    [TestMethod]
    public void Validate_ZeroValue_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Items[0].Value = 0;
        Assert.IsTrue(HasError(Sut.Validate(op), "értéke"), "0 tétel-érték → üzleti szabály hiba (NAV 2021-től)");
    }

    // ---- Séma-szint (generált DataAnnotations) ------------------------------

    [TestMethod]
    public void Validate_InvalidVtszPattern_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Items[0].ProductVtsz = "12"; // pattern [0-9]{4,8} → túl rövid
        Assert.IsTrue(HasError(Sut.Validate(op), "productVtsz"), "rossz VTSZ-formátum → séma hiba");
    }

    [TestMethod]
    public void Validate_NullVtsz_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Items[0].ProductVtsz = null; // a Required kapja el
        Assert.IsTrue(HasError(Sut.Validate(op), "productVtsz"), "null VTSZ → Required hiba");
    }

    [TestMethod]
    public void Validate_EmptyVtsz_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Items[0].ProductVtsz = ""; // a séma átengedné — az üzleti szabály kapja el
        Assert.IsTrue(HasError(Sut.Validate(op), "productVtsz"), "üres VTSZ → üzleti szabály hiba");
    }

    [TestMethod]
    public void Validate_InvalidPlateNumber_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Vehicle!.PlateNumber = "AB"; // MinLength 4 / pattern sérül
        var errors = Sut.Validate(op);
        Assert.IsTrue(HasError(errors, "plateNumber") || HasError(errors, "vehicle"), "túl rövid rendszám → séma hiba");
    }

    [TestMethod]
    public void Validate_MissingProductName_ReportsError()
    {
        var op = ValidOperation();
        op.TradeCard!.Items[0].ProductName = null; // [Required(AllowEmptyStrings=false)]
        Assert.IsTrue(HasError(Sut.Validate(op), "productName"), "hiányzó terméknév → séma hiba");
    }

    // ---- Lista / művelet-szint ----------------------------------------------

    [TestMethod]
    public void Validate_EmptyList_ReportsError()
    {
        var errors = Sut.Validate(new List<TradeCardOperationType>());
        Assert.IsTrue(HasError(errors, "legalább egy"), "üres lista → hiba");
    }

    [TestMethod]
    public void Validate_DeleteWithoutTradeCard_DoesNotRequireTradeCard()
    {
        var op = new TradeCardOperationType { Index = 1, Operation = OperationType.Delete, Tcn = "ABC123" };
        Assert.IsFalse(HasError(Sut.Validate(op), "a tradeCard kötelező"), "delete + Tcn esetén a tradeCard hiánya nem hiba");
    }

    [TestMethod]
    public void Validate_NonDeleteWithoutTradeCard_ReportsError()
    {
        var op = new TradeCardOperationType { Index = 1, Operation = OperationType.Create, TradeCard = null };
        Assert.IsTrue(HasError(Sut.Validate(op), "tradeCard"), "create esetén a tradeCard kötelező");
    }

    // ---- Hibalista-jelleg: minden hibát összegyűjt --------------------------

    [TestMethod]
    public void Validate_CollectsAllErrors_DoesNotStopAtFirst()
    {
        var op = ValidOperation();
        op.TradeCard!.Vehicle = null;       // üzleti
        op.TradeCard.Items.Clear();          // üzleti
        op.TradeCard.SellerName = null;      // üzleti
        op.TradeCard.DestinationVatNumber = null; // üzleti

        var errors = Sut.Validate(op);
        Assert.IsTrue(errors.Count >= 4, $"minden hibát összegyűjt, nem áll meg az elsőnél; kapott {errors.Count}");
    }

    [TestMethod]
    public void Validate_IndexedPath_WhenMultipleOperations()
    {
        var bad = ValidOperation();
        bad.TradeCard!.Vehicle = null;
        var errors = Sut.Validate([ValidOperation(), bad]);
        Assert.IsTrue(errors.Any(e => e.ErrorMessage?.Contains("[1]") == true), "a hibák a művelet indexével prefixeltek");
    }
}
