using FluentAssertions;
using Microsoft.Extensions.Time.Testing;
using SIGCM2.Domain.Entities;

namespace SIGCM2.Application.Tests.Domain.ProductTypes;

public class ProductTypeTests
{
    private static readonly FakeTimeProvider FakeTime =
        new(new DateTimeOffset(2026, 4, 19, 10, 0, 0, TimeSpan.Zero));

    // ── ForCreation: happy path ──────────────────────────────────────────────

    [Fact]
    public void ForCreation_ValidInputs_ReturnsNewInstance_WithActiveTrue()
    {
        var pt = ProductType.ForCreation(
            "Clasificados",
            hasDuration: true, requiresText: true, requiresCategory: false, isBundle: false,
            allowImages: false,
            maxImages: null, maxImageSizeMB: null, maxImageWidth: null, maxImageHeight: null,
            FakeTime);

        pt.Id.Should().Be(0);
        pt.Nombre.Should().Be("Clasificados");
        pt.HasDuration.Should().BeTrue();
        pt.RequiresText.Should().BeTrue();
        pt.RequiresCategory.Should().BeFalse();
        pt.IsBundle.Should().BeFalse();
        pt.AllowImages.Should().BeFalse();
        pt.IsActive.Should().BeTrue();
        pt.FechaCreacion.Should().Be(FakeTime.GetUtcNow().UtcDateTime);
        pt.FechaModificacion.Should().BeNull();
    }

    // ── ForCreation: Nombre validations ──────────────────────────────────────

    [Fact]
    public void ForCreation_NombreNull_ThrowsArgumentException()
    {
        var act = () => ProductType.ForCreation(
            null!, false, false, false, false, false,
            null, null, null, null, FakeTime);

        act.Should().Throw<ArgumentException>();
    }

    [Fact]
    public void ForCreation_NombreWhitespace_ThrowsArgumentException()
    {
        var act = () => ProductType.ForCreation(
            "   ", false, false, false, false, false,
            null, null, null, null, FakeTime);

        act.Should().Throw<ArgumentException>();
    }

    [Fact]
    public void ForCreation_NombreOver200Chars_ThrowsArgumentException()
    {
        var nombre = new string('X', 201);

        var act = () => ProductType.ForCreation(
            nombre, false, false, false, false, false,
            null, null, null, null, FakeTime);

        act.Should().Throw<ArgumentException>();
    }

    // ── ForCreation: multimedia normalization ────────────────────────────────

    [Fact]
    public void ForCreation_AllowImagesFalse_NormalizesAll4MultimediaFieldsToNull()
    {
        var pt = ProductType.ForCreation(
            "Clasificados",
            false, false, false, false,
            allowImages: false,
            maxImages: 5, maxImageSizeMB: 2.5m, maxImageWidth: 1920, maxImageHeight: 1080,
            FakeTime);

        pt.MaxImages.Should().BeNull();
        pt.MaxImageSizeMB.Should().BeNull();
        pt.MaxImageWidth.Should().BeNull();
        pt.MaxImageHeight.Should().BeNull();
    }

    [Fact]
    public void ForCreation_AllowImagesTrue_PreservesMultimediaValues()
    {
        var pt = ProductType.ForCreation(
            "Fotos",
            false, false, false, false,
            allowImages: true,
            maxImages: 10, maxImageSizeMB: 5.0m, maxImageWidth: 800, maxImageHeight: 600,
            FakeTime);

        pt.AllowImages.Should().BeTrue();
        pt.MaxImages.Should().Be(10);
        pt.MaxImageSizeMB.Should().Be(5.0m);
        pt.MaxImageWidth.Should().Be(800);
        pt.MaxImageHeight.Should().Be(600);
    }

    [Fact]
    public void ForCreation_AllowImagesTrue_AllMaxNull_IsValid()
    {
        var pt = ProductType.ForCreation(
            "Fotos sin límite",
            false, false, false, false,
            allowImages: true,
            maxImages: null, maxImageSizeMB: null, maxImageWidth: null, maxImageHeight: null,
            FakeTime);

        pt.AllowImages.Should().BeTrue();
        pt.MaxImages.Should().BeNull();
        pt.MaxImageSizeMB.Should().BeNull();
        pt.MaxImageWidth.Should().BeNull();
        pt.MaxImageHeight.Should().BeNull();
    }

    // ── WithRenamed ──────────────────────────────────────────────────────────

    [Fact]
    public void WithRenamed_ValidNombre_ReturnsNewInstance_WithFechaModificacionUpdated()
    {
        var original = ProductType.ForCreation("Original", false, false, false, false, false, null, null, null, null, FakeTime);
        var updated = FakeTimeProvider2();
        var renamed = original.WithRenamed("Nuevo", updated);

        renamed.Nombre.Should().Be("Nuevo");
        renamed.FechaModificacion.Should().Be(updated.GetUtcNow().UtcDateTime);
    }

    [Fact]
    public void WithRenamed_DoesNotMutateOriginal()
    {
        var original = ProductType.ForCreation("Original", false, false, false, false, false, null, null, null, null, FakeTime);
        _ = original.WithRenamed("Nuevo", FakeTime);

        original.Nombre.Should().Be("Original");
        original.FechaModificacion.Should().BeNull();
    }

    // ── WithUpdatedFlags ─────────────────────────────────────────────────────

    [Fact]
    public void WithUpdatedFlags_SetsFlags_PreservesMultimedia()
    {
        var original = ProductType.ForCreation(
            "Tipo", false, false, false, false,
            allowImages: true, maxImages: 5, maxImageSizeMB: 2.0m, maxImageWidth: null, maxImageHeight: null,
            FakeTime);
        var updated = original.WithUpdatedFlags(true, true, true, true, FakeTime);

        updated.HasDuration.Should().BeTrue();
        updated.RequiresText.Should().BeTrue();
        updated.RequiresCategory.Should().BeTrue();
        updated.IsBundle.Should().BeTrue();
        updated.AllowImages.Should().BeTrue();
        updated.MaxImages.Should().Be(5);
        updated.MaxImageSizeMB.Should().Be(2.0m);
    }

    // ── WithUpdatedMultimedia ─────────────────────────────────────────────────

    [Fact]
    public void WithUpdatedMultimedia_AllowImagesFalse_NullifiesAllLimits()
    {
        var original = ProductType.ForCreation(
            "Tipo", false, false, false, false,
            allowImages: true, maxImages: 5, maxImageSizeMB: 2.0m, maxImageWidth: 1024, maxImageHeight: 768,
            FakeTime);
        var updated = original.WithUpdatedMultimedia(false, 3, 1.0m, 800, 600, FakeTime);

        updated.AllowImages.Should().BeFalse();
        updated.MaxImages.Should().BeNull();
        updated.MaxImageSizeMB.Should().BeNull();
        updated.MaxImageWidth.Should().BeNull();
        updated.MaxImageHeight.Should().BeNull();
    }

    [Fact]
    public void WithUpdatedMultimedia_AllowImagesTrue_PreservesLimits()
    {
        var original = ProductType.ForCreation("Tipo", false, false, false, false, false, null, null, null, null, FakeTime);
        var updated = original.WithUpdatedMultimedia(true, 8, 3.5m, 1920, 1080, FakeTime);

        updated.AllowImages.Should().BeTrue();
        updated.MaxImages.Should().Be(8);
        updated.MaxImageSizeMB.Should().Be(3.5m);
        updated.MaxImageWidth.Should().Be(1920);
        updated.MaxImageHeight.Should().Be(1080);
    }

    // ── WithDeactivated ──────────────────────────────────────────────────────

    [Fact]
    public void WithDeactivated_SetsIsActiveFalse_AndFechaModificacion()
    {
        var original = ProductType.ForCreation("Tipo", false, false, false, false, false, null, null, null, null, FakeTime);
        var tp2 = FakeTimeProvider2();
        var deactivated = original.WithDeactivated(tp2);

        deactivated.IsActive.Should().BeFalse();
        deactivated.FechaModificacion.Should().Be(tp2.GetUtcNow().UtcDateTime);
        original.IsActive.Should().BeTrue(); // original not mutated
    }

    // ── Hydration ────────────────────────────────────────────────────────────

    [Fact]
    public void Constructor_HydrationRoundtrip_PreservesAllFields()
    {
        var fechaCreacion = new DateTime(2026, 1, 15, 8, 0, 0, DateTimeKind.Utc);
        var fechaModificacion = new DateTime(2026, 3, 10, 9, 30, 0, DateTimeKind.Utc);

        var pt = new ProductType(
            id: 42,
            nombre: "Bundle Aniversario",
            hasDuration: true,
            requiresText: false,
            requiresCategory: true,
            isBundle: true,
            allowImages: true,
            maxImages: 12,
            maxImageSizeMB: 2.75m,
            maxImageWidth: 1920,
            maxImageHeight: 1080,
            isActive: false,
            fechaCreacion: fechaCreacion,
            fechaModificacion: fechaModificacion);

        pt.Id.Should().Be(42);
        pt.Nombre.Should().Be("Bundle Aniversario");
        pt.HasDuration.Should().BeTrue();
        pt.RequiresText.Should().BeFalse();
        pt.RequiresCategory.Should().BeTrue();
        pt.IsBundle.Should().BeTrue();
        pt.AllowImages.Should().BeTrue();
        pt.MaxImages.Should().Be(12);
        pt.MaxImageSizeMB.Should().Be(2.75m);
        pt.MaxImageWidth.Should().Be(1920);
        pt.MaxImageHeight.Should().Be(1080);
        pt.IsActive.Should().BeFalse();
        pt.FechaCreacion.Should().Be(fechaCreacion);
        pt.FechaModificacion.Should().Be(fechaModificacion);
    }

    // ── Helper ───────────────────────────────────────────────────────────────

    private static FakeTimeProvider FakeTimeProvider2() =>
        new(new DateTimeOffset(2026, 4, 20, 15, 0, 0, TimeSpan.Zero));
}
