using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using Mapster;
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 readonly ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository;
    private readonly IWellOperationCategoryRepository wellOperationCategoryRepository;
    private readonly IWellService wellService;
    private readonly IWellOperationService wellOperationService;

    /// <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 },
         { (2, 5002), 5113 },
         { (2, 5003), 5113 },
         { (3, 5096), 5084 },
         { (3, 5008), 5084 },
         { (3, 5085), 5015 },
         { (3, 5014), 5015 },
         { (3, 5002), 5113 },
         { (3, 5003), 5113 },
         { (31, 5014), 5015 },
         { (31, 5012), 5013 },
         { (31, 5083), 5013 },
         { (31, 5002), 5113 },
         { (31, 5003), 5113 },
         { (4, 5085), 5015 },
         { (4, 5087), 5015 },
         { (4, 5014), 5015 },
         { (4, 5053), 5037 },
         { (4, 5084), 5096 },
         { (4, 5086), 5013 },
         { (4, 5002), 5113 },
         { (4, 5003), 5113 },
         { (6, 5085), 5015 },
         { (6, 5036), 5034 },
         { (6, 5035), 5097 },
         { (6, 5002), 5113 },
         { (6, 5003), 5113 },
         { (6, 5021), 5095 },
         { (6, 5086), 5012 }

    };

    private HashSet<(int IdSectionType, int IdCategory)> WellSectionTypesWithCategories = new HashSet<(int IdSectionType, int IdCategory)>()
    {
        { (2, 5001) },
        { (2, 5113) },
        { (2, 5013) },
        { (2, 5000) },
        { (2, 5022) },
        { (2, 5017) },
        { (2, 5023) },
        { (2, 4007) },
        { (2, 5090) },
        { (3, 5001) },
        { (3, 5015) },
        { (3, 5037) },
        { (3, 5057) },
        { (3, 5113) },
        { (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, 5113) },
        { (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, 5113) },
        { (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, 5113) },
        { (6, 5036) },
        { (6, 5013) },
        { (6, 5000) },
        { (6, 5022) },
        { (6, 5093) },
        { (6, 5017) },
        { (6, 5095) },
        { (6, 5012) },
        { (6, 5040) },
        { (6, 5092) },

        { (5, 5001) },
        { (5, 5015) },
        { (5, 5046) },
        { (5, 5037) },
        { (5, 5097) },
        { (5, 5057) },
        { (5, 5113) },
        { (5, 5036) },
        { (5, 5008) },
        { (5, 5003) },
        { (5, 5036) },
        { (5, 5013) },
        { (5, 5000) },
        { (5, 5029) },
        { (5, 5022) },
        { (5, 5017) },
        { (5, 5019) },
        { (5, 5042) },
        { (5, 5046) },

        { (2, 5096) },
        { (2, 5008) },
        { (2, 5002) },
        { (2, 5003) },

        { (3, 5096) },
        { (3, 5008) },
        { (3, 5002) },
        { (3, 5003) },
        { (3, 5085) },
        { (3, 5014) },

        { (31, 5002) },
        { (31, 5003) },
        { (31, 5014) },
        { (31, 5012) },
        { (31, 5083) },

        { (4, 5002) },
        { (4, 5003) },
        { (4, 5085) },
        { (4, 5087) },
        { (4, 5014) },
        { (4, 5053) },
        { (4, 5084) },
        { (4, 5086) },

        { (6, 5002) },
        { (6, 5003) },
        { (6, 5085) },
        { (6, 5036) },
        { (6, 5035) },
        { (6, 5021) },
        { (6, 5086) },

    };

    public WellCompositeOperationService(
        ICrudRepository<WellSectionTypeDto> wellSectionTypeRepository,
        IWellOperationCategoryRepository wellOperationCategoryRepository,
        IWellService wellService,
        IWellOperationService wellOperationService)
    {
        this.wellSectionTypeRepository = wellSectionTypeRepository;
        this.wellOperationCategoryRepository = wellOperationCategoryRepository;
        this.wellService = wellService;
        this.wellOperationService = wellOperationService;
    }

    public async Task<WellCompositeOperationDto> 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 wells = await wellService.GetAsync(new WellRequest { Ids = idsWells }, token);
        var wellsDict = wells.ToDictionary(w => w.Id);

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

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

        var operationsForComposite = operations.Select(o => CreateCompositeOperation(o, sectionsDict, categoriesDict));

        var wellOperationsWithComposite = new List<Dictionary<int, WellOperationDto>>();
        var compositeDepth = 0d;
        var compositeDay = 0d;
        var result = new WellCompositeOperationDto();

        var compositeOperations = new List<WellOperationDto>();
        foreach ((int IdSection, int IdCategory) in WellSectionTypesWithCategories)
        {
            var filteredByTemplate = operationsForComposite
                .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 WellOperationDto
            {
                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 compositeOperation = aggreagtedByWell.OrderBy(o => o.DurationHours).
                ThenByDescending(o => o.DepthStart)
                .First();

            compositeOperation.IdWell = 0;

            if (compositeDepth > compositeOperation.DepthStart)
                compositeOperation.DepthStart = compositeDepth;
            compositeDepth = compositeOperation.DepthStart;

            compositeOperation.Day = compositeDay / 24;
            compositeDay += compositeOperation.DurationHours;

            compositeOperations.Add(compositeOperation);
        }

        var groupedByWellOperations = operations
          .GroupBy(o => o.IdWell)
          .ToDictionary(o => o.Key, o => o.ToArray())
          .Select(o => new WellCompositeOperationSourceDto()
          {
              Operations = o.Value,
              Well = wellsDict[o.Key],
          });

        result.WellOperationsComposite = compositeOperations;
        result.WellCompositeSourceOperations = groupedByWellOperations;

        return result;
    }

    private static WellOperationDto CreateCompositeOperation(
        WellOperationDto dto,
        IDictionary<int, string> sectionTypes,
        IDictionary<int, string> operationCategories)
    {
        var newDto = dto.Adapt<WellOperationDto>();
        if (newDto.IdWellSectionType == wellSectionTransportTable)
        {
            newDto.IdWellSectionType = wellSectionProductionString;
            newDto.WellSectionTypeCaption = sectionTypes[newDto.IdWellSectionType];
        }

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

        return newDto;
    }
}