using FluentValidation;
using TakeoutSaaS.Application.App.Finance.Cost.Commands;
using TakeoutSaaS.Domain.Finance.Enums;

namespace TakeoutSaaS.Application.App.Finance.Cost.Validators;

/// <summary>
/// 成本录入保存验证器。
/// </summary>
public sealed class SaveFinanceCostEntryCommandValidator : AbstractValidator<SaveFinanceCostEntryCommand>
{
    /// <summary>
    /// 初始化验证规则。
    /// </summary>
    public SaveFinanceCostEntryCommandValidator()
    {
        RuleFor(x => x.Dimension)
            .Must(value => value is FinanceCostDimension.Tenant or FinanceCostDimension.Store)
            .WithMessage("dimension 非法");

        RuleFor(x => x)
            .Must(command =>
                command.Dimension != FinanceCostDimension.Store ||
                (command.StoreId.HasValue && command.StoreId.Value > 0))
            .WithMessage("storeId 非法");

        RuleFor(x => x.CostMonth)
            .Must(IsMonthInExpectedRange)
            .WithMessage("month 非法");

        RuleFor(x => x.Categories)
            .NotNull()
            .Must(categories => categories.Count > 0)
            .WithMessage("categories 不能为空");

        RuleFor(x => x.Categories)
            .Must(HaveDistinctCategories)
            .WithMessage("分类重复");

        RuleForEach(x => x.Categories)
            .SetValidator(new SaveFinanceCostCategoryCommandItemValidator());
    }

    private static bool HaveDistinctCategories(IReadOnlyList<SaveFinanceCostCategoryCommandItem> categories)
    {
        var normalized = (categories ?? [])
            .Select(item => item.Category)
            .Where(value => value != default)
            .ToList();

        return normalized.Count == normalized.Distinct().Count();
    }

    private static bool IsMonthInExpectedRange(DateTime value)
    {
        var month = value.Kind switch
        {
            DateTimeKind.Utc => value,
            DateTimeKind.Local => value.ToUniversalTime(),
            _ => DateTime.SpecifyKind(value, DateTimeKind.Utc)
        };

        return month.Year is >= 2000 and <= 2100;
    }
}

/// <summary>
/// 成本分类保存项验证器。
/// </summary>
public sealed class SaveFinanceCostCategoryCommandItemValidator : AbstractValidator<SaveFinanceCostCategoryCommandItem>
{
    /// <summary>
    /// 初始化验证规则。
    /// </summary>
    public SaveFinanceCostCategoryCommandItemValidator()
    {
        RuleFor(x => x.Category)
            .Must(value => value is FinanceCostCategory.FoodMaterial
                or FinanceCostCategory.Labor
                or FinanceCostCategory.FixedExpense
                or FinanceCostCategory.PackagingConsumable)
            .WithMessage("category 非法");

        RuleFor(x => x.TotalAmount)
            .GreaterThanOrEqualTo(0);

        RuleForEach(x => x.Items)
            .SetValidator(new SaveFinanceCostDetailCommandItemValidator());
    }
}

/// <summary>
/// 成本明细保存项验证器。
/// </summary>
public sealed class SaveFinanceCostDetailCommandItemValidator : AbstractValidator<SaveFinanceCostDetailCommandItem>
{
    /// <summary>
    /// 初始化验证规则。
    /// </summary>
    public SaveFinanceCostDetailCommandItemValidator()
    {
        RuleFor(x => x.ItemName)
            .NotEmpty()
            .MaximumLength(64);

        RuleFor(x => x.Amount)
            .GreaterThanOrEqualTo(0);

        RuleFor(x => x.Quantity)
            .GreaterThanOrEqualTo(0)
            .When(x => x.Quantity.HasValue);

        RuleFor(x => x.UnitPrice)
            .GreaterThanOrEqualTo(0)
            .When(x => x.UnitPrice.HasValue);

        RuleFor(x => x.SortOrder)
            .GreaterThanOrEqualTo(0);
    }
}
