using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Services
{
    public class WellCompositeOperationService : IWellCompositeOperationService
    {
        private ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository;
        private IWellOperationCategoryRepository wellOperationCategoryRepository;
        private IWellOperationRepository wellOperationRepository;

        /// <summary>
        /// Тип секции "Транспортный стол"
        /// </summary>
        private const int wellSectionTransportTable = 5;

        /// <summary>
        /// Тип секции "Эксплуатационная колонна"
        /// </summary>
        private const int wellSectionProductionString = 4;


        /// <summary>
        /// набор настроек для замены одной категории секции на другую
        /// </summary>
        private static Dictionary<(int, int), int> SettingsForSectionCategoryChange = new Dictionary<(int, int), int>() {
             { (2, 5096), 5013 },
             { (2, 5008), 5013 },
             { (3, 5096), 5084 },
             { (3, 5008), 5084 },
             { (3, 5085), 5015 },
             { (3, 5014), 5015 },
             { (31, 5014), 5015 },
             { (31, 5012), 5013 },
             { (31, 5083), 5013 },
             { (4, 5085), 5015 },
             { (4, 5087), 5015 },
             { (4, 5014), 5015 },
             { (4, 5053), 5037 },
             { (4, 5084), 5096 },
             { (4, 5086), 5013 },
             { (6, 5085), 5015 },
             { (6, 5036), 5034 },
             { (6, 5035), 5097 }
        };

        private HashSet<(int IdSectionType, int IdCategory)> WellSectionTypesWithCategories = new HashSet<(int IdSectionType, int IdCategory)>()
        {
            { (2, 5001) },
            { (2, 5003) },
            { (2, 5013) },
            { (2, 5000) },
            { (2, 5022) },
            { (2, 5017) },
            { (2, 5023) },
            { (2, 4007) },
            { (2, 5090) },
            { (3, 5001) },
            { (3, 5015) },
            { (3, 5037) },
            { (3, 5057) },
            { (3, 5003) },
            { (3, 5036) },
            { (3, 5084) },
            { (3, 5013) },
            { (3, 5000) },
            { (3, 5022) },
            { (3, 5017) },
            { (3, 4007) },
            { (3, 5090) },
            { (3, 5045) },
            { (3, 5042) },
            { (3, 5046) },
            { (31, 5001) },
            { (31, 5015) },
            { (31, 5037) },
            { (31, 5057) },
            { (31, 5003) },
            { (31, 5036) },
            { (31, 5013) },
            { (31, 5022) },
            { (31, 5017) },
            { (31, 5023) },
            { (31, 4007) },
            { (31, 5045) },
            { (31, 5042) },
            { (31, 5046) },
            { (4, 5001) },
            { (4, 5015) },
            { (4, 5046) },
            { (4, 5037) },
            { (4, 5097) },
            { (4, 5057) },
            { (4, 5003) },
            { (4, 5036) },
            { (4, 5008) },
            { (4, 5003) },
            { (4, 5036) },
            { (4, 5013) },
            { (4, 5000) },
            { (4, 5029) },
            { (4, 5022) },
            { (4, 5017) },
            { (4, 5019) },
            { (4, 5042) },
            { (4, 5046) },
            { (6, 5001) },
            { (6, 5015) },
            { (6, 5034) },
            { (6, 5037) },
            { (6, 5097) },
            { (6, 5057) },
            { (6, 5003) }
        };



        public WellCompositeOperationService(
            ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository,
            IWellOperationCategoryRepository wellOperationCategoryRepository,
            IWellOperationRepository wellOperationRepository)
        {
            this.wellSectionTypeRepository = wellSectionTypeRepository;
            this.wellOperationCategoryRepository = wellOperationCategoryRepository;
            this.wellOperationRepository = wellOperationRepository;
        }

        public async Task<List<Dictionary<int, WellOperationDataDto>>> GetAsync(IEnumerable<int> idsWells, CancellationToken token)
        {
            var sections = await wellSectionTypeRepository.GetAllAsync(token);
            var sectionsDict = sections.ToDictionary(s => s.Id, s => s.Caption);

            var categories = wellOperationCategoryRepository.Get(true);
            var categoriesDict = categories.ToDictionary(s => s.Id, s => s.Name);

            var idsWellSectionTypes = WellSectionTypesWithCategories.Select(t => t.IdSectionType).Distinct();
            var usedCategories = WellSectionTypesWithCategories.Select(c => c.IdCategory).Distinct();

            var wellOperationRequest = new WellsOperationRequest()
            {
                IdsWell = idsWells,
                OperationCategoryIds = usedCategories,
                SectionTypeIds = idsWellSectionTypes,
                OperationType = WellOperation.IdOperationTypeFact
            };
            var operations = await wellOperationRepository.GetAsync(wellOperationRequest, token);

            var renamedOperations = operations.Select(o => UpdateIdWellSectionAndIdCategory(o, sectionsDict, categoriesDict));

            var wellOperationsWithComposite = new List<Dictionary<int, WellOperationDataDto>>();
            var compositeDepth = 0d;
            foreach ((int IdSection, int IdCategory) in WellSectionTypesWithCategories)
            {
                var filteredByTemplate = renamedOperations
                    .Where(o => o.IdWellSectionType == IdSection)
                    .Where(o => o.IdCategory == IdCategory);

                if (!filteredByTemplate.Any())
                    continue;

                var groupedByWell = filteredByTemplate.GroupBy(o => o.IdWell);

                var aggreagtedByWell = groupedByWell.Select(g => new WellOperationDataDto
                {
                    IdCategory = IdCategory,
                    IdWell = g.Key,
                    IdWellSectionType = IdSection,
                    DepthStart = g.Min(o => o.DepthStart),
                    DurationHours = g.Sum(o => o.DurationHours),
                    OperationCategoryName = g.First().OperationCategoryName,
                    WellSectionTypeCaption = g.First().WellSectionTypeCaption,
                });

                var composite = aggreagtedByWell.OrderBy(o => o.DurationHours).
                        ThenByDescending(o => o.DepthStart)
                        .First();

                composite.IdWell = 0;
                if (compositeDepth > composite.DepthStart)
                    composite.DepthStart = compositeDepth;

                compositeDepth = composite.DepthStart;

                var resultItem = aggreagtedByWell.ToDictionary(o => o.IdWell);
                resultItem.Add(0, composite);

                wellOperationsWithComposite.Add(resultItem);
            }
            return wellOperationsWithComposite;
        }

        private static WellOperationDataDto UpdateIdWellSectionAndIdCategory(
            WellOperationDataDto dto,
            Dictionary<int, string> sectionTypes,
            Dictionary<int, string> operationCategories)
        {
            if (dto.IdWellSectionType == wellSectionTransportTable)
            {
                dto.IdWellSectionType = wellSectionProductionString;
                dto.WellSectionTypeCaption = sectionTypes[dto.IdWellSectionType] ?? string.Empty;
            }

            if ((SettingsForSectionCategoryChange.TryGetValue((dto.IdWellSectionType, dto.IdCategory), out int newIdCategory)))
            {
                dto.IdCategory = newIdCategory;
                dto.OperationCategoryName = operationCategories[dto.IdCategory] ?? string.Empty;
            }

            return dto;
        }
    }
}