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

namespace AsbCloudInfrastructure.Services;

public class WellboreService : IWellboreService
{
	private readonly IWellService wellService;
	private readonly IWellOperationRepository wellOperationRepository;

	public WellboreService(IWellService wellService, IWellOperationRepository wellOperationRepository)
	{
		this.wellService = wellService;
		this.wellOperationRepository = wellOperationRepository;
	}

	public async Task<WellboreDto?> GetWellboreAsync(int idWell, int idSection, CancellationToken cancellationToken)
	{
		var request = new WellboreRequest
		{
			Ids = new (int, int?)[] { (idWell, idSection) },
			Take = 1,
		};
		var data = await GetWellboresAsync(request, cancellationToken);
		return data.FirstOrDefault();
    }

	public async Task<IEnumerable<WellboreDto>> GetWellboresAsync(WellboreRequest request,
		CancellationToken cancellationToken)
	{
		var wellbores = new List<WellboreDto>(request.Ids.Count());
		var skip = request.Skip ?? 0;
		var take = request.Take ?? 10;

		var sections = wellOperationRepository.GetSectionTypes()
			.ToDictionary(w => w.Id, w => w);

		var ids = request.Ids.GroupBy(i => i.idWell);

        foreach (var id in ids)
		{
			var well = await wellService.GetOrDefaultAsync(id.Key, cancellationToken);

			if (well is null)
				continue;

            var wellOperations = await GetFactOperationsAsync(well.Id, id.Select(i => i.idSection), cancellationToken);
			var groupedOperations = wellOperations.GroupBy(o => o.IdWellSectionType);
			var wellWellbores = groupedOperations.Select(group => new WellboreDto {
				Id = group.Key,
				Name = sections[group.Key].Caption,
                Well = well.Adapt<WellWithTimezoneDto>(),
                DateStart = group.Min(operation => operation.DateStart),
				DateEnd = group.Max(operation => operation.DateStart.AddHours(operation.DurationHours)),
				DepthStart = group.Min(operation => operation.DepthStart),
				DepthEnd = group.Max(operation => operation.DepthEnd),
            });
            wellbores.AddRange(wellWellbores);
		}

		return wellbores
			.OrderBy(w => w.Well.Id).ThenBy(w => w.Id)
			.Skip(skip).Take(take);
	}

	private async Task<IOrderedEnumerable<WellOperationDto>> GetFactOperationsAsync(int idWell, IEnumerable<int?> idsSections,
		CancellationToken cancellationToken)
	{
		var request = new WellOperationRequest
		{
			IdWell = idWell,
			OperationType = WellOperation.IdOperationTypeFact,
			SortFields = new[] { "DateStart asc" },
		};

        request.SectionTypeIds = idsSections.All(i => i.HasValue)
			? idsSections.Select(i => i!.Value)
			: null;

		return (await wellOperationRepository.GetAsync(request, cancellationToken))
			.OrderBy(o => o.DateStart);
	}
}