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

namespace AsbCloudInfrastructure.Repository;

public class DepositRepository : IDepositRepository
{
    private readonly IAsbCloudDbContext db;
    private readonly ITelemetryService telemetryService;

    public DepositRepository(IAsbCloudDbContext db, ITelemetryService telemetryService)
    {
        this.db = db;
        this.telemetryService = telemetryService;
    }

    /// <inheritdoc/>
    public async Task<IEnumerable<DepositDto>> GetAsync(int idCompany,
        CancellationToken token = default)
    {
        var wellsQuery = db.Set<Well>()
            .Include(w => w.RelationCompaniesWells)
            .Include(w => w.WellType)
            .Include(w => w.Cluster)
            .ThenInclude(c => c.Deposit)
            .Where(well => well.RelationCompaniesWells.Any(r => r.IdCompany == idCompany));
        
        var wellEntities = await wellsQuery.ToArrayAsync(token);

        var gDepositEntities = GroupWells(wellEntities);

        var dtos = CreateDepositDto(gDepositEntities)
            .ToArray();

        return dtos;
    }

    /// <inheritdoc/>
    public async Task<IEnumerable<ClusterDto>> GetClustersAsync(int idCompany,
        int depositId, CancellationToken token = default)
    {
        var entities = await GetWellsForCompany(idCompany)
                    .Select(e => e.Cluster)
                    .Where(e => e.IdDeposit == depositId)
                    .Distinct()
                    .AsNoTracking()
                    .ToListAsync(token)
                    .ConfigureAwait(false);

        var dtos = entities.Adapt<IEnumerable<ClusterDto>>();

        return dtos;
    }

    private static IEnumerable<IGrouping<Deposit, IGrouping<Cluster, Well>>> GroupWells(IEnumerable<Well> wellEntities)
    => wellEntities
            .GroupBy(w => w.Cluster)
            .GroupBy(c => c.Key.Deposit);

    private IQueryable<Well> GetWellsForCompany(int idCompany)
    => db.Set<Well>()
            .Include(w => w.RelationCompaniesWells)
            .ThenInclude(r => r.Company)
            .Include(w => w.Cluster)
            .ThenInclude(c => c.Deposit)
            .Where(w => w.RelationCompaniesWells.Any(c => c.IdCompany == idCompany));

    private IEnumerable<DepositDto> CreateDepositDto(IEnumerable<IGrouping<Deposit, IGrouping<Cluster, Well>>> gDepositEntities)
    {
        var dtos = gDepositEntities.Select(gDeposit => new DepositDto
        {
            Id = gDeposit.Key.Id,
            Caption = gDeposit.Key.Caption,
            Latitude = gDeposit.Key.Latitude,
            Longitude = gDeposit.Key.Longitude,
            Clusters = gDeposit.Select(gCluster => new ClusterDto
            {
                Id = gCluster.Key.Id,
                Caption = gCluster.Key.Caption,
                Latitude = gCluster.Key.Latitude,
                Longitude = gCluster.Key.Longitude,
                Wells = gCluster.Select(well =>
                {
                    var dto = well.Adapt<WellDto>();
                    dto.WellType = well.WellType.Caption;

                    dto.LastTelemetryDate = DateTimeOffset.MinValue;

                    if (well.IdTelemetry != null)
                        dto.LastTelemetryDate = telemetryService.GetDatesRange(well.IdTelemetry.Value).To;

                    dto.Cluster = gCluster.Key.Caption;
                    dto.Deposit = gDeposit.Key.Caption;
                    return dto;
                }),
            }),
        });
        return dtos;
    }
}