using FluentValidation;
using LiteCqrs;
using TeleWave.Application.Common.Models;
using TeleWave.Domain.Programming;

namespace TeleWave.Application.Programming.Templates.Junctions;

public sealed record ListJunctionsQuery : IQuery<IReadOnlyList<JunctionTemplateDto>>;

public sealed record CreateJunctionCommand(string Name) : ICommand<Result<Guid>>;

public sealed record UpdateJunctionCommand(Guid JunctionId, string Name, int? MaxTotalSeconds)
    : ICommand<Result>;

public sealed record DeleteJunctionCommand(Guid JunctionId) : ICommand<Result>;

public sealed record AddJunctionElementCommand(Guid JunctionId, JunctionElementKind Kind)
    : ICommand<Result<Guid>>;

/// <summary>Полный набор настроек врезки — то же тело для правки любого элемента стыка.</summary>
public sealed record JunctionElementInput(
    JunctionElementKind Kind,
    Guid? GroupId,
    Guid? BumperTemplateId,
    Guid? BumperVariantId,
    JunctionAmountMode AmountMode,
    int AmountValue,
    bool IsRequired,
    /// <summary>Метка развилки: из врезок с одной меткой играет одна, выбранная по весам.</summary>
    string? ChoiceKey,
    int ChoiceWeight,
    JunctionConditions? Conditions
);

public sealed record UpdateJunctionElementCommand(
    Guid JunctionId,
    Guid ElementId,
    JunctionElementInput Input
) : ICommand<Result>;

public sealed record RemoveJunctionElementCommand(Guid JunctionId, Guid ElementId)
    : ICommand<Result>;

/// <summary>
/// Позиция врезки вместе с её развилкой: перетаскивание в цепочке одновременно меняет и порядок,
/// и принадлежность к развилке, поэтому отдельной команды «сгруппировать» нет.
/// </summary>
public sealed record JunctionElementOrder(Guid ElementId, string? ChoiceKey);

public sealed record ReorderJunctionCommand(
    Guid JunctionId,
    IReadOnlyList<JunctionElementOrder> Order
) : ICommand<Result>;

public sealed class CreateJunctionCommandValidator : AbstractValidator<CreateJunctionCommand>
{
    public CreateJunctionCommandValidator() => RuleFor(x => x.Name).NotEmpty().MaximumLength(128);
}

public sealed class UpdateJunctionCommandValidator : AbstractValidator<UpdateJunctionCommand>
{
    public UpdateJunctionCommandValidator()
    {
        RuleFor(x => x.Name).NotEmpty().MaximumLength(128);
        // Потолок стыка в сутки — верхняя граница здравого смысла, а не техническое ограничение.
        RuleFor(x => x.MaxTotalSeconds)
            .InclusiveBetween(1, 24 * 60 * 60)
            .When(x => x.MaxTotalSeconds is not null);
    }
}

public sealed class UpdateJunctionElementCommandValidator
    : AbstractValidator<UpdateJunctionElementCommand>
{
    public UpdateJunctionElementCommandValidator()
    {
        // Верхняя граница — сутки: врезка длиннее вещательного дня бессмысленна.
        RuleFor(x => x.Input.AmountValue).InclusiveBetween(1, 24 * 60);
        RuleFor(x => x.Input.ChoiceKey).MaximumLength(64);
        RuleFor(x => x.Input.ChoiceWeight).InclusiveBetween(0, 1000);
        RuleFor(x => x.Input.Conditions!.MinMinutesBetween)
            .InclusiveBetween(0, 24 * 60)
            .When(x => x.Input.Conditions is not null);
        RuleFor(x => x.Input.Conditions!.Chance)
            .InclusiveBetween(0, 100)
            .When(x => x.Input.Conditions is not null);
        // Допуск больше получаса перестаёт быть привязкой к часу: он покрывает час целиком.
        RuleFor(x => x.Input.Conditions!.NearHourMinutes)
            .InclusiveBetween(0, 30)
            .When(x => x.Input.Conditions is not null);
    }
}
