using Cronos;
using FluentValidation;
using IWS5.Core.DTO.IAdmin.Schedules;
using IWS5.DatabaseAccess.Interfaces.IAdmin;
using IWS5.DatabaseAccess.Repositories.IAdmin;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using System.Globalization;
using System.Text.RegularExpressions;

namespace IWS5.Services.Validators.IAdmin.Schedule;

public class UpdateScheduleRequestValidator : AbstractValidator<ScheduleUpdateRequestDTO>
{
    public UpdateScheduleRequestValidator(IScheduleRepository scheduleRepository, IActionContextAccessor actionContextAccessor, IServerRepository serverRepository)
    {
        RuleFor(x => x.Name)
            .NotEmpty().WithMessage("Schedule name is required.")
            .MaximumLength(255).WithMessage("Schedule name must not exceed 255 characters.")
            .Must(name =>
            {
                try
                {
                    var scheduleId = Convert.ToInt32((string)actionContextAccessor.ActionContext.RouteData.Values.Where(o => o.Key == "scheduleId").Select(o => o.Value).FirstOrDefault());
                    var schedule = scheduleRepository.GetByIdAsync(scheduleId).GetAwaiter().GetResult();
                    if (schedule.Name == name)
                    {
                        return true;
                    }

                    return !scheduleRepository.ExistsByNameAsync(name).GetAwaiter().GetResult();
                }
                catch { return false; }
            })
            .WithMessage("Schedule name must be unique.");

        RuleFor(x => x.ServerHost)
            .NotEmpty().WithMessage("ServerHost is required.")
            .MaximumLength(255).WithMessage("ServerHost must not exceed 255 characters.")
            .Must(name =>
            {
                try
                {
                    var server = serverRepository.GetByNameAsync(name).GetAwaiter().GetResult();
                    return server != null;
                }
                catch { return false; }
            })
            .WithMessage("ServerHost must match a valid server instance.");

        RuleFor(x => x.Description)
            .MaximumLength(255).WithMessage("Description must not exceed 255 characters.");

        RuleFor(x => x.Priority)
            .GreaterThanOrEqualTo(0).WithMessage("Priority cannot be negative.");

        RuleFor(x => x.StatusId)
            .GreaterThan(0).WithMessage("StatusId must be valid.");

        RuleFor(x => x.ScheduleTypeId)
            .GreaterThan(0).WithMessage("ScheduleTypeId must be a valid positive value.");

        When(x => x.ScheduleTypeId == 1, () =>
        {
            RuleFor(x => x.RecurrenceExpression)
                .Must(expr => string.IsNullOrWhiteSpace(expr))
                .WithMessage("RecurrenceExpression must be empty for OneTime schedules.");
        });

        When(x => x.ScheduleTypeId != 1, () =>
        {
            RuleFor(x => x.RecurrenceExpression)
                .NotEmpty().WithMessage("RecurrenceExpression is required for recurring schedules.")
                .Must(expr => CronExpression.TryParse(expr, out var _))
                .WithMessage("RecurrenceExpression must be parseable as a cron expression.");
        });

        RuleFor(x => x.StartTime)
            .Must(date => date == null || date >= DateTime.UtcNow)
            .WithMessage("Start time must be current or future time.");

        RuleFor(x => x.ExpireTime)
            .Must((dto, expire) => expire == null || dto.StartTime == null || expire > dto.StartTime)
            .WithMessage("Expire time must be after Start time.");
    }
}
