using FluentValidation;
using Microsoft.EntityFrameworkCore;
using PARR.API.Contracts.V1.Requests;
using PARR.Core.Repositories.Interfaces;
using PARR.Core.Repositories.Interfaces.Job;
using PARR.Core.Repositories.Interfaces.Schedule;
using PARR.Core.Repositories.Interfaces.Unit;
using PARR.Domain.Enums;

namespace PARR.API.Validators
{
    public class JobGroupValidator : AbstractValidator<JobGroupRequest>
    {
        public JobGroupValidator(
            IJobGroupTypeRepository jobGroupTypeService,
            IUnitFieldRepository unitFieldService,
            IScheduleExcludeTypeRepository scheduleExcludeTypeService,
            IScheduleExcludeTypeCalendarRepository scheduleExcludeTypeCalendarService,
            IDistributionPeriodRepository distributionPeriodService
            )
        {
            RuleFor(t => t.Name)
                .NotNull().NotEmpty();

            RuleFor(t => t.ShortDescription)
                .NotNull().NotEmpty();

            RuleFor(t => t.FullDescription)
                .NotNull().NotEmpty();

            RuleFor(t => t.Solution)
                .NotNull().NotEmpty();

            RuleFor(t => t.TemplateDuration)
                .NotNull().NotEmpty();

            RuleFor(t => t.GroupTypeId)
                .MustAsync(async (entity, value, c) => await jobGroupTypeService.GetAsync(value) != null)
                .WithMessage("Указан несуществующий Id типа");

            //Проверяем существует ли такой GroupingUnitFieldId в unitField
            RuleFor(t => t.GroupingUnitFieldId)
                .MustAsync(async (entity, value, c) =>
                {
                    if (!value.HasValue)
                    {
                        return true;
                    }

                    return await unitFieldService.GetAsync(value.Value) != null;
                })
                .WithMessage("Указано несуществующий Id поля");


            RuleFor(t => t.GroupingUnitFieldId)
                .MustAsync(async (entity, value, c) =>
                {
                    var groupingJobType = await jobGroupTypeService.Get().FirstAsync(t => t.Code == JobGroupTypesEnum.Group);

                    // Если это сгруппированный тип, то у него обязательно должно быть заполнено поле GroupingUnitFieldId
                    if (entity.GroupTypeId == groupingJobType.Id)
                        return value.HasValue;

                    return true;
                })
                .WithMessage("Не указано поле для группировки");

            RuleFor(t => t.ScheduleExcludeTypeId)
                .NotNull()
                .NotEmpty()
                .MustAsync(async (entity, value, c) => await scheduleExcludeTypeService.GetAsync(value) != null)
                .WithMessage("Некорректное значение");

            RuleFor(t => t.ScheduleExcludeTypeCalendarId)
                .MustAsync(async (entity, value, c) =>
                {
                    // Если выбрано "Нет исключений", то это поле должно быть пустое, иначе, должно быть валидное значение
                    var type = await scheduleExcludeTypeService.GetAsync(entity.ScheduleExcludeTypeId);
                    if (type == null)
                        return false;

                    if (type.Code == ScheduleExcludeTypeEnum.None.ToString())
                    {
                        // ScheduleExcludeTypeCalendarId должно быть null 
                        return value == null;
                    }

                    // Тип любой кроме "Нет исключений", значение обязательно
                    if (!value.HasValue)
                        return false;

                    return await scheduleExcludeTypeCalendarService.GetAsync(value.Value) != null;
                })
                .WithMessage("Некорректное значение");

            RuleFor(t => t.DistributionConfig)
                .Must((entity, value, c) =>
                {
                    // если включено автораспределение, должны быть настройки
                    if (entity.IsAutoDistributionEnabled && value != null)
                        return true;

                    // если выкл автораспределение, то валидно
                    if (!entity.IsAutoDistributionEnabled)
                        return true;

                    return false;
                }).WithMessage("Отсутствуют настройки автораспределения");

            RuleFor(t => t.DistributionConfig)
                .MustAsync(async (entity, value, c) =>
                {
                    // если есть настройка периода, проверить что она валидна
                    var periodId = value?.DistributionPeriodId;

                    if (periodId.HasValue)
                    {
                        var exist = await distributionPeriodService.GetAsync(periodId.Value);

                        return exist != null;
                    }

                    return true;
                }).WithMessage("Некорректное значение периода распределения");

            RuleFor(t => t.UserTimeZoneOffsetMinutes)
                    .Must((entity, value, c) =>
                    {
                        // если IsResponseAreaTimezone == true, то поле UserTimeZoneOffsetMinutes обязательно.
                        // если IsResponseAreaTimezone == false, то UserTimeZoneOffsetMinutes должно быть null
                        return (entity.IsWorkGroupTimezone && value != null) || (!entity.IsWorkGroupTimezone && value == null);
                    }).WithMessage("Некорректное значение."); ;

        }
    }
}
