using FluentValidation.TestHelper;
using Microsoft.Extensions.Time.Testing;
using SIGCM2.Application.Pricing.ChargeableChars.Create;
using SIGCM2.Domain.Pricing.ChargeableChars;

namespace SIGCM2.Application.Tests.Pricing.ChargeableChars;

/// <summary>
/// PRC-001 — Validator tests for CreateChargeableCharConfigCommand.
/// Covers: Symbol length, Category enum, PricePerUnit > 0, ValidFrom >= today_AR.
/// </summary>
public class CreateChargeableCharConfigCommandValidatorTests
{
    private readonly FakeTimeProvider _time = new(new DateTimeOffset(2026, 4, 20, 12, 0, 0, TimeSpan.Zero));
    private readonly CreateChargeableCharConfigCommandValidator _validator;

    private static readonly DateOnly Today = new(2026, 4, 20);
    private static readonly DateOnly Yesterday = new(2026, 4, 19);
    private static readonly DateOnly Tomorrow = new(2026, 4, 21);

    public CreateChargeableCharConfigCommandValidatorTests()
    {
        _validator = new CreateChargeableCharConfigCommandValidator(_time);
    }

    private static CreateChargeableCharConfigCommand ValidCmd() => new(
        ProductTypeId: null,
        Symbol: "$",
        Category: ChargeableCharCategories.Currency,
        PricePerUnit: 1.0m,
        ValidFrom: Today);

    // ── Symbol ───────────────────────────────────────────────────────────────────

    [Fact]
    public void Symbol_Empty_FailsValidation()
    {
        var cmd = ValidCmd() with { Symbol = "" };
        _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Symbol);
    }

    [Fact]
    public void Symbol_TooLong_FailsValidation()
    {
        // max 4 chars
        var cmd = ValidCmd() with { Symbol = "ABCDE" };
        _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Symbol);
    }

    [Fact]
    public void Symbol_SingleChar_Passes()
    {
        var cmd = ValidCmd() with { Symbol = "$" };
        _validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.Symbol);
    }

    [Fact]
    public void Symbol_FourChars_Passes()
    {
        var cmd = ValidCmd() with { Symbol = "ABCD" };
        _validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.Symbol);
    }

    // ── Category ─────────────────────────────────────────────────────────────────

    [Fact]
    public void Category_Invalid_FailsValidation()
    {
        var cmd = ValidCmd() with { Category = "Unknown" };
        _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Category);
    }

    [Fact]
    public void Category_Empty_FailsValidation()
    {
        var cmd = ValidCmd() with { Category = "" };
        _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Category);
    }

    [Theory]
    [InlineData("Currency")]
    [InlineData("Percentage")]
    [InlineData("Exclamation")]
    [InlineData("Question")]
    [InlineData("Other")]
    public void Category_ValidValues_Pass(string category)
    {
        var cmd = ValidCmd() with { Category = category };
        _validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.Category);
    }

    // ── PricePerUnit ─────────────────────────────────────────────────────────────

    [Fact]
    public void PricePerUnit_Zero_Passes_OptInBilling()
    {
        // PRC-001 followup #57: opt-in billing — 0 is a valid price meaning
        // "this Symbol exists but does NOT charge extra for this ProductType".
        // Aligned with DB check constraint CK_ChargeableCharConfig_Price_NonNegative (>= 0).
        var cmd = ValidCmd() with { PricePerUnit = 0m };
        _validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.PricePerUnit);
    }

    [Fact]
    public void PricePerUnit_Negative_FailsValidation()
    {
        var cmd = ValidCmd() with { PricePerUnit = -1m };
        _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.PricePerUnit);
    }

    [Fact]
    public void PricePerUnit_Positive_Passes()
    {
        var cmd = ValidCmd() with { PricePerUnit = 0.01m };
        _validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.PricePerUnit);
    }

    // ── Symbol emoji rejection (PRC-001 followup #55) ───────────────────────────

    [Theory]
    [InlineData("😀")]          // smiley face (U+1F600, Emoticons block)
    [InlineData("🚗")]          // car (U+1F697, Transport block)
    [InlineData("🔥")]          // fire (U+1F525, Misc Symbols & Pictographs)
    [InlineData("❤️")]          // red heart + VS-16 (U+2764 U+FE0F)
    [InlineData("☀️")]          // sun (U+2600 in Misc Symbols block)
    public void Symbol_WithEmoji_FailsValidation(string emojiSymbol)
    {
        var cmd = ValidCmd() with { Symbol = emojiSymbol };
        _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.Symbol);
    }

    [Theory]
    [InlineData("$")]
    [InlineData("%")]
    [InlineData("!")]
    [InlineData("¡")]
    [InlineData("@")]
    [InlineData("€")]
    [InlineData("##")]
    [InlineData("ABCD")]
    public void Symbol_WithoutEmoji_Passes(string plainSymbol)
    {
        var cmd = ValidCmd() with { Symbol = plainSymbol };
        _validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.Symbol);
    }

    // ── ValidFrom ────────────────────────────────────────────────────────────────

    [Fact]
    public void ValidFrom_InPast_FailsValidation()
    {
        var cmd = ValidCmd() with { ValidFrom = Yesterday };
        _validator.TestValidate(cmd).ShouldHaveValidationErrorFor(x => x.ValidFrom);
    }

    [Fact]
    public void ValidFrom_Today_Passes()
    {
        var cmd = ValidCmd() with { ValidFrom = Today };
        _validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.ValidFrom);
    }

    [Fact]
    public void ValidFrom_Future_Passes()
    {
        var cmd = ValidCmd() with { ValidFrom = Tomorrow };
        _validator.TestValidate(cmd).ShouldNotHaveValidationErrorFor(x => x.ValidFrom);
    }

    // ── Happy path ────────────────────────────────────────────────────────────────

    [Fact]
    public void ValidCommand_PassesAllRules()
    {
        _validator.TestValidate(ValidCmd()).ShouldNotHaveAnyValidationErrors();
    }
}
