using FluentValidation;
using MediatR;

namespace DocumentOperator.Application.AddStamp;

/// <summary>
/// Command to add a stamp (text, image, or predefined) to PDF pages.
/// Combines Command, Handler, and Validator in a single file (Vertical Slice pattern).
/// </summary>
public record AddStampCommand : IRequest<byte[]>
{
    public required Stream PdfStream { get; init; }
    public required Domain.Models.ValueObjects.StampType StampType { get; init; }
    public int[]? PageNumbers { get; init; } // null = all pages
    public required (double X, double Y) Position { get; init; }
    public (double Width, double Height)? Size { get; init; }
    public Domain.Models.ValueObjects.AnnotationOrigin Origin { get; init; } = Domain.Models.ValueObjects.AnnotationOrigin.BottomLeft;
    public string? Text { get; init; }
    public string? FontName { get; init; }
    public double? FontSize { get; init; }
    public string? Color { get; init; }
    public double? Opacity { get; init; }
    public double? Rotation { get; init; }
    public Domain.Models.ValueObjects.StampPlacement Placement { get; init; } = Domain.Models.ValueObjects.StampPlacement.Foreground;
    public byte[]? ImageBytes { get; init; }
    public Domain.Models.ValueObjects.PredefinedStampType? PredefinedType { get; init; }
}

/// <summary>
/// Handler for AddStampCommand.
/// </summary>
public class AddStampHandler(Common.Interfaces.IPdfProcessor pdfProcessor) : IRequestHandler<AddStampCommand, byte[]>
{
    public async Task<byte[]> Handle(AddStampCommand command, CancellationToken cancellationToken)
    {
        return await pdfProcessor.AddStampAsync(
            command.PdfStream,
            command.StampType,
            command.PageNumbers,
            command.Position,
            command.Size,
            command.Origin,
            command.Text,
            command.FontName,
            command.FontSize,
            command.Color,
            command.Opacity,
            command.Rotation,
            command.Placement,
            command.ImageBytes,
            command.PredefinedType
        );
    }
}

/// <summary>
/// Validator for AddStampCommand.
/// </summary>
public class AddStampValidator : AbstractValidator<AddStampCommand>
{
    public AddStampValidator()
    {
        RuleFor(x => x.PdfStream)
            .NotNull().WithMessage("PDF stream is required");

        RuleFor(x => x.Position.X)
            .GreaterThanOrEqualTo(0).WithMessage("Position X must be >= 0");

        RuleFor(x => x.Position.Y)
            .GreaterThanOrEqualTo(0).WithMessage("Position Y must be >= 0");

        // Text stamp validation
        When(x => x.StampType == Domain.Models.ValueObjects.StampType.Text, () =>
        {
            RuleFor(x => x.Text)
                .NotEmpty().WithMessage("Text is required for Text stamp type");
        });

        // Image stamp validation
        When(x => x.StampType == Domain.Models.ValueObjects.StampType.Image, () =>
        {
            RuleFor(x => x.ImageBytes)
                .NotNull().WithMessage("Image bytes are required for Image stamp type")
                .Must(bytes => bytes != null && bytes.Length > 0).WithMessage("Image bytes cannot be empty");
        });

        // Predefined stamp validation
        When(x => x.StampType == Domain.Models.ValueObjects.StampType.Predefined, () =>
        {
            RuleFor(x => x.PredefinedType)
                .NotNull().WithMessage("Predefined type is required for Predefined stamp type");
        });

        // Optional parameter validations
        When(x => x.Opacity.HasValue, () =>
        {
            RuleFor(x => x.Opacity!.Value)
                .InclusiveBetween(0.0, 1.0).WithMessage("Opacity must be between 0.0 and 1.0");
        });

        When(x => x.Rotation.HasValue, () =>
        {
            RuleFor(x => x.Rotation!.Value)
                .InclusiveBetween(0.0, 360.0).WithMessage("Rotation must be between 0 and 360 degrees");
        });

        When(x => x.FontSize.HasValue, () =>
        {
            RuleFor(x => x.FontSize!.Value)
                .GreaterThan(0).WithMessage("Font size must be positive");
        });

        When(x => !string.IsNullOrWhiteSpace(x.Color), () =>
        {
            RuleFor(x => x.Color!)
                .Matches(@"^[0-9A-Fa-f]{6}$").WithMessage("Color must be 6-digit hex (e.g., 'FF0000')");
        });

        When(x => x.PageNumbers != null, () =>
        {
            RuleFor(x => x.PageNumbers!)
                .Must(pages => pages.All(p => p > 0)).WithMessage("All page numbers must be >= 1");
        });
    }
}
