using FluentAssertions;
using FluentValidation.TestHelper;
using foodmarket.Api.Controllers.Organizations;
using foodmarket.Api.Controllers.Purchases;
using foodmarket.Api.Controllers.Sales;
using foodmarket.Api.Infrastructure.Validation;
using foodmarket.Application.Catalog;
using Xunit;

namespace foodmarket.UnitTests;

public class SupplyInputValidatorTests
{
    private readonly SupplyInputValidator _v = new();

    [Fact]
    public void Rejects_empty_supplier()
    {
        var input = new SuppliesController.SupplyInput(
            DateTime.UtcNow, Guid.Empty, Guid.NewGuid(), Guid.NewGuid(),
            "", new[] { new SuppliesController.SupplyLineInput(Guid.NewGuid(), 1m, 100m) });
        _v.TestValidate(input).ShouldHaveValidationErrorFor(x => x.SupplierId);
    }

    [Fact]
    public void Rejects_empty_lines()
    {
        var input = new SuppliesController.SupplyInput(
            DateTime.UtcNow, Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(),
            "", Array.Empty<SuppliesController.SupplyLineInput>());
        _v.TestValidate(input).ShouldHaveValidationErrorFor(x => x.Lines);
    }

    [Fact]
    public void Rejects_zero_quantity_in_line()
    {
        var input = new SuppliesController.SupplyInput(
            DateTime.UtcNow, Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(),
            "", new[] { new SuppliesController.SupplyLineInput(Guid.NewGuid(), 0m, 100m) });
        _v.TestValidate(input).ShouldHaveValidationErrorFor("Lines[0].Quantity");
    }

    [Fact]
    public void Rejects_future_date()
    {
        var input = new SuppliesController.SupplyInput(
            DateTime.UtcNow.AddDays(5), Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(),
            "", new[] { new SuppliesController.SupplyLineInput(Guid.NewGuid(), 1m, 100m) });
        _v.TestValidate(input).ShouldHaveValidationErrorFor(x => x.Date);
    }

    [Fact]
    public void Accepts_valid()
    {
        var input = new SuppliesController.SupplyInput(
            DateTime.UtcNow.AddHours(-1), Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(),
            "Notes", new[] { new SuppliesController.SupplyLineInput(Guid.NewGuid(), 5m, 100m) });
        _v.TestValidate(input).ShouldNotHaveAnyValidationErrors();
    }
}

public class RetailSaleInputValidatorTests
{
    private readonly RetailSaleInputValidator _v = new();

    [Fact]
    public void Rejects_negative_paid()
    {
        var input = new RetailSalesController.RetailSaleInput(
            DateTime.UtcNow, Guid.NewGuid(), null, null, Guid.NewGuid(),
            foodmarket.Domain.Sales.PaymentMethod.Cash,
            PaidCash: -1m, PaidCard: 0m,
            Notes: "", Lines: new[] {
                new RetailSalesController.RetailSaleLineInput(Guid.NewGuid(), 1m, 100m, 0m, 12m)
            });
        _v.TestValidate(input).ShouldHaveValidationErrorFor(x => x.PaidCash);
    }

    [Fact]
    public void Rejects_empty_lines()
    {
        var input = new RetailSalesController.RetailSaleInput(
            DateTime.UtcNow, Guid.NewGuid(), null, null, Guid.NewGuid(),
            foodmarket.Domain.Sales.PaymentMethod.Cash, 0m, 0m, "",
            Array.Empty<RetailSalesController.RetailSaleLineInput>());
        _v.TestValidate(input).ShouldHaveValidationErrorFor(x => x.Lines);
    }
}

public class ProductInputValidatorTests
{
    private readonly ProductInputValidator _v = new();

    private static ProductInput Make(string name = "Молоко", decimal vat = 12,
        Guid? unitId = null, decimal? minStock = null, decimal? maxStock = null) => new(
        Name: name, Article: null, Description: null,
        UnitOfMeasureId: unitId ?? Guid.NewGuid(),
        Vat: vat, VatEnabled: true,
        ProductGroupId: Guid.NewGuid(),
        DefaultSupplierId: null, CountryOfOriginId: null,
        IsService: false, Packaging: foodmarket.Domain.Catalog.Packaging.Piece, IsMarked: false,
        MinStock: minStock, MaxStock: maxStock,
        ReferencePrice: null,
        PurchaseCurrencyId: null,
        ImageUrl: null,
        Prices: Array.Empty<ProductPriceInput>(),
        Barcodes: Array.Empty<ProductBarcodeInput>());

    [Fact]
    public void Empty_name_rejected() =>
        _v.TestValidate(Make(name: "")).ShouldHaveValidationErrorFor(x => x.Name);

    [Fact]
    public void Vat_out_of_range_rejected() =>
        _v.TestValidate(Make(vat: 150m)).ShouldHaveValidationErrorFor(x => x.Vat);

    [Fact]
    public void Min_greater_than_max_rejected()
    {
        var r = _v.TestValidate(Make(minStock: 10, maxStock: 5));
        r.Errors.Should().Contain(e => e.ErrorMessage.Contains("MinStock"));
    }

    [Fact]
    public void Valid_passes() =>
        _v.TestValidate(Make()).ShouldNotHaveAnyValidationErrors();
}

public class CounterpartyInputValidatorTests
{
    private readonly CounterpartyInputValidator _v = new();

    [Fact]
    public void Bin_must_be_12_digits()
    {
        var bad = new CounterpartyInput("ТОО", null, foodmarket.Domain.Catalog.CounterpartyType.LegalEntity,
            Bin: "123", Iin: null, TaxNumber: null, CountryId: null,
            Address: null, Phone: null, Email: null,
            BankName: null, BankAccount: null, Bik: null, ContactPerson: null, Notes: null);
        _v.TestValidate(bad).ShouldHaveValidationErrorFor(x => x.Bin);
    }

    [Fact]
    public void Invalid_email_rejected()
    {
        var bad = new CounterpartyInput("ТОО", null, foodmarket.Domain.Catalog.CounterpartyType.LegalEntity,
            Bin: "123456789012", Iin: null, TaxNumber: null, CountryId: null,
            Address: null, Phone: null, Email: "not-an-email",
            BankName: null, BankAccount: null, Bik: null, ContactPerson: null, Notes: null);
        _v.TestValidate(bad).ShouldHaveValidationErrorFor(x => x.Email);
    }
}

public class EmployeeInputValidatorTests
{
    private readonly EmployeeInputValidator _v = new();

    [Fact]
    public void SendInvite_without_email_rejected()
    {
        var bad = new EmployeesController.EmployeeInput(
            "Иванов", "Иван", null, null, Email: null, null, null, null, null, null,
            Guid.NewGuid(), true, null, CreateAccount: true, SendInvite: true);
        _v.TestValidate(bad).ShouldHaveValidationErrorFor(x => x.Email);
    }

    [Fact]
    public void CreateAccount_without_email_rejected()
    {
        var bad = new EmployeesController.EmployeeInput(
            "Иванов", "Иван", null, null, Email: null, null, null, null, null, null,
            Guid.NewGuid(), true, null, CreateAccount: true);
        _v.TestValidate(bad).ShouldHaveValidationErrorFor(x => x.Email);
    }

    [Fact]
    public void Valid_passes()
    {
        var ok = new EmployeesController.EmployeeInput(
            "Иванов", "Иван", null, null, "iv@x.kz", null, null, null, null, null,
            Guid.NewGuid(), true, null, CreateAccount: true, SendInvite: true);
        _v.TestValidate(ok).ShouldNotHaveAnyValidationErrors();
    }
}
