using FluentValidation;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using PARR.API.Contracts.V1.Requests;
using PARR.DAL.Services.Interfaces;

namespace PARR.API.Validators
{
    public class ApplicationInWorkValidator : AbstractValidator<ApplicationInWorkRequest>
    {
        private readonly IApplicationsInWorkService applicationsInWorkService;
        private readonly IEsppSchTypeConfigService esppSchTypeConfigService;
        private readonly IWorkGroupService workGroupService;
        private readonly IEsppSchTypeValueService esppSchTypeValueService;

        public ApplicationInWorkValidator(
            IApplicationsInWorkService applicationsInWorkService,
            IEsppSchTypeConfigService esppSchTypeConfigService,
            IWorkGroupService workGroupService,
            IEsppSchTypeValueService esppSchTypeValueService
            )
        {
            this.applicationsInWorkService = applicationsInWorkService;
            this.esppSchTypeConfigService = esppSchTypeConfigService;
            this.workGroupService = workGroupService;
            this.esppSchTypeValueService = esppSchTypeValueService;

            RuleFor(t => t.TemplateDuration)
                .NotNull().NotEmpty()//.WithMessage("Длительность данного задания на выполнение работ не может быть пустым")
                .Matches("^\\d{1,2}\\s\\d{1,2}[:]\\d{1,2}[:]\\d{1,2}$")
                //.WithMessage("Длительность данного задания на выполнение работ должна соответствовать шаблону dd hh:mm:ss(7 00:00:00)");
                .WithMessage("Значение не соответствует шаблону dd hh:mm:ss(7 00:00:00)");

            RuleFor(t => t.ShortDescription)
                .NotNull().NotEmpty();//.WithMessage("Краткое описание данного задания на выполнение работ не может быть пустым");

            RuleFor(t => t.FullDescription)
                .NotNull().NotEmpty();//.WithMessage("Подробное описание данного задания на выполнение работ не может быть пустым");

            RuleFor(t => t.Solution)
                .NotNull().NotEmpty();//.WithMessage("Решение данного задания на выполнение работ не может быть пустым");

            RuleFor(t => t.WorkId)
                .MustAsync(async (entity, value, c) => await IsWorkExist(entity))
                .WithMessage("Указан несуществующий Id работы");
            //.WithMessage("У данного задания на выполнение работ указан несуществующий Id работы");

            RuleFor(t => t.ApplicationId)
                .MustAsync(async (entity, value, c) => await IsApplicationExist(entity))
                    .WithMessage("Указан несуществующий Id программного обеспечения");
            //.MustAsync(async (entity, value, c) => await AppInWorkIsUnique(entity))
            //    .WithMessage("Задание на выполнение работ с такими ПО и работой уже существует");
            //.WithMessage("У данного задания на выполнение работ указан несуществующий Id программного обеспечения");



            #region old schedule
            //Проверяем настройки планировщика
            //RuleFor(t => t.Schedule).NotNull().NotEmpty()
            //    .WithMessage("Настройки планировщика задания на выполнение работ не могут быть пустыми");

            //RuleFor(t => t.Schedule.EsppSchValues)
            //    .NotNull().NotEmpty()
            //        .When(t => t.Schedule != null)
            //            .WithMessage("Настройки планировщика задания на выполнение работ не могут быть пустыми")
            //    .Must((entity, value, c) => IsEsppSchValuesExist(entity))
            //        .WithMessage("Не удалось найти подходящую конфигруцию планировщика задания на выполнение работ");
            #endregion

            //RuleFor(t => t.Schedule)
            //   .NotNull().NotEmpty()
            //       .When(t => t.Schedule.Count() > 0)
            //           .WithMessage("Настройки планировщика не могут быть пустыми")
            //   .Must((entity, value, c) => IsEsppSchValuesExist(entity.Schedule))
            //       .WithMessage("Не удалось найти подходящую конфигруцию планировщика задания");

            #region comment

            //RuleFor(t => t.Schedule.Values)
            //    .NotNull().NotEmpty().WithMessage($"Настройки значений планировщика задания на выполнение работ не могут быть пустыми")
            //    .Must((entity, value, c) => IsScheduleTypeValueIdExist(entity))
            //    .When(t => t.Schedule != null).WithMessage("Не удалось найти подходящую конфигруцию планировщика задания на выполнение работ");

            //RuleFor(t => t.Schedule.TypeScheduleId)
            //    .NotNull().NotEmpty().WithMessage($"Тип планировщика задания на выполнение работ не может быть пустым")
            //    .Must((entity, value, c) => IsTypeScheduleIdExist(entity))
            //    .When(t => t.Schedule != null).WithMessage($"Не удалось найти указанный тип планировщика задания на выполнение работ");

            //RuleFor(t => t.Schedule.Values)
            //    .NotNull().NotEmpty().WithMessage($"Настройки значений планировщика задания на выполнение работ не могут быть пустыми")
            //    .Must((entity, value, c) => IsScheduleTypeValueIdExist(entity))
            //    .When(t => t.Schedule != null).WithMessage("Не удалось найти подходящую конфигруцию планировщика задания на выполнение работ");

            #endregion


            RuleFor(t => t.WorkGroups)
                .MustAsync(async (entity, value, c) => await IsWorkGroupsExistAsync(entity))
                .WithMessage("Указаные несуществующие Id рабочих групп");


            //Автораспределение может быть включено, только если у EsppSchTypeValues не пустое поле DistributionPeriodId
            RuleFor(t => t.IsAutoDistributionEnabled)
                .MustAsync(async (entity, value, c) => await IsAllowAutoDistributionEnabledAsync(entity, value))
                .WithMessage("Нельзя включить автораспределение для этого типа расписания");
        }


        private async Task<bool> IsAllowAutoDistributionEnabledAsync(ApplicationInWorkRequest entity, bool value)//TODO JobGroupRequest
        {
            //Автораспределение может быть включено, только если у EsppSchTypeValues не пустое поле DistributionPeriodId

            if (value == false)
                return true;

            //Распределение включено, смотрим, разрешено ли оно в расписании
            //по идее, это расписание только с одним значением в EsppSchValues, но мы проверим у всех, но такого быть не может по хорошему
            //foreach (var item in entity.Schedule)
            //{
            //    var schVal = await esppSchTypeValueService.GetAsync(item.TypeValueId);
            //    if (schVal != null)
            //        if (schVal.DistributionPeriodId == null)
            //            return false;
            //}

            return true;
        }


        private async Task<bool> IsWorkGroupsExistAsync(ApplicationInWorkRequest entity)
        {
            var result = await workGroupService.Get().Where(t => entity.WorkGroups.Contains(t.Id)).ToListAsync();

            return entity.WorkGroups.Count() == result.Count();
        }

        //private bool IsEsppSchValuesExist(ApplicationInWorkRequest request)
        private bool IsEsppSchValuesExist(List<EsppSchValueRequest> listSchedule)
        {
            var configs = esppSchTypeConfigService.GetWithSchIncludes().ToList();

            //Проверяем полученные Id конфигураций и Id конфигураций в базе
            var typeConfigIdList = listSchedule.Select(t => t.TypeConfigId);
            var foundTypeConfigIdList = configs.Where(t => typeConfigIdList.Contains(t.Id)).Distinct();

            if (foundTypeConfigIdList.Count() == 0                                                                                      //Не найдено совпадений с базой
                || foundTypeConfigIdList.Select(t => t.TypeScheduleId).Distinct().Count() > 1)                                          //или имеют разный тип планировщика
                return false;

            var typeScheduleId = foundTypeConfigIdList.Select(t => t.TypeScheduleId).FirstOrDefault();

            if (foundTypeConfigIdList.Count() != configs.Where(t => t.TypeScheduleId == typeScheduleId).Count())                        //не все typeConfig найдены в базе
                return false;

            //Теперь проверяем значения
            foreach (var item in listSchedule)
            {
                var curTypeConfig = configs.Where(t => t.Id == item.TypeConfigId).Single();

                var isCurValueExist = curTypeConfig!.EsppSchType!.EsppSchTypeValues.Where(t => t.Id == item.TypeValueId).Any();

                if (!isCurValueExist)
                    return false;
            }

            return true;
        }

        //private async Task<bool> AppInWorkIsUnique(ApplicationInWorkRequest request)
        //{
        //    //уникальная запись по полям ApplicationId, WorkId

        //    var existSameAiW = await applicationsInWorkService.GetAsync(request.ApplicationId, request.WorkId);

        //    //TODO: тут спорно, нужна ли эта проверка? Мы же можем создать несколько РР на одно и тоже ПО но с разным расписанием?
        //    //к тому же, в методе update, такой проверки нет! 
        //    //if (existSameAiW != null)
        //    //    return BadRequest(new Response(false, new List<ErrorModel> {
        //    //        new ErrorModel { Message = $"Уже существует задание на выполнение работ для программного обеспечения id({request.ApplicationId}) и работой id({request.WorkId})." } }
        //    //    ));

        //    return existSameAiW == null;
        //}

        private async Task<bool> IsWorkExist(ApplicationInWorkRequest request)
        {
            //return await workService.GetAsync(request.WorkId) != null;
            return true;
        }


        private async Task<bool> IsApplicationExist(ApplicationInWorkRequest request)
        {
            //return await workService.GetAsync(request.WorkId) != null;
            return true;
        }


        //private bool IsTypeScheduleIdExist(ApplicationInWorkRequest request)
        //{
        //    var configs = esppSchTypeConfigService.GetWithSchIncludes().ToList();
        //var result = configs.Where(t => t.TypeId == request.Schedule.TypeScheduleId).FirstOrDefault() != null;
        //return result;
        //    return false;
        //}


        //private bool IsScheduleTypeValueIdExist(ApplicationInWorkRequest request)
        //{
        //    var configs = esppSchTypeConfigService.GetWithSchIncludes().ToList();

        //    var mustValues = configs.Where(t => t.TypeScheduleId == request.Schedule.TypeScheduleId).ToList();
        //    var values = request.Schedule.Values;

        //    foreach (var mustValue in mustValues)
        //    {
        //        var mlValues = mustValue?.EsppSchType?.EsppSchTypeValues.Select(t => t.Id);

        //        if (mlValues == null)
        //            return false;

        //        if (!mlValues.Intersect(values).Any())
        //            return false;
        //    }

        //    return true;
        //}

    }
}
