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

namespace AsbCloudInfrastructure.Services
{
    public class WellService : IWellService
    {
        private readonly IAsbCloudDbContext db;
        private readonly ITelemetryTracker telemetryTracker;
        private readonly CacheTable<RelationCompanyWell> cacheRelationCompaniesWells;
        private readonly CacheTable<Well> cacheWells;

        public WellService(IAsbCloudDbContext db, ITelemetryTracker telemetryTracker, CacheDb cacheDb)
        {
            this.db = db;
            this.telemetryTracker = telemetryTracker;
            cacheRelationCompaniesWells = cacheDb.GetCachedTable<RelationCompanyWell>((AsbCloudDbContext)db);
            cacheWells = cacheDb.GetCachedTable<Well>((AsbCloudDbContext)db);
        }

        public async Task<IEnumerable<WellDto>> GetTransmittingWellsAsync(int idCompany, CancellationToken token)
        {
            var wells = new List<Well>();
            IEnumerable<string> activeTelemetriesUids = telemetryTracker.GetTransmittingTelemetryUids();
            if (activeTelemetriesUids.Any())
            {
                wells = await db.GetWellsForCompany(idCompany)
                    .Where(w => activeTelemetriesUids.Contains(w.Telemetry.RemoteUid))
                    .AsNoTracking()
                    .ToListAsync(token)
                    .ConfigureAwait(false);
            }
            return wells.Select(w => From(w));
        }

        public async Task<IEnumerable<WellDto>> GetWellsByCompanyAsync(int idCompany, CancellationToken token)
        {
            var wells = await db.GetWellsForCompany(idCompany).ToListAsync(token);
            return wells.Select(w => From(w));
        }

        public async Task<bool> IsCompanyInvolvedInWellAsync(int idCompany, int idWell, CancellationToken token)
         => await cacheRelationCompaniesWells.ContainsAsync(r => r.IdWell == idWell &&
                r.IdCompany == idCompany, token).ConfigureAwait(false);

        private static WellDto From(Well well)
        {
            var wellDto = new WellDto
            {
                Id = well.Id,
                Caption = well.Caption,
                Cluster = well.Cluster.Caption,
                Deposit = well.Cluster.Deposit.Caption,
            };

            return wellDto;
        }

        public async Task<WellDto> GetAsync(int idWell, CancellationToken token)
        {
            var entity = await db.Wells
                .Include(w => w.Cluster)
                .ThenInclude(c => c.Deposit)
                .FirstOrDefaultAsync(w => w.Id == idWell, token)
                .ConfigureAwait(false);

            var dto = entity.Adapt<WellDto>();
            dto.Cluster = entity.Cluster?.Caption;
            dto.Deposit = entity.Cluster?.Deposit?.Caption;
            return dto;
        }
        public async Task<string> GetWellCaptionByIdAsync(int idWell, CancellationToken token)
        {
            var entity = await cacheWells.FirstOrDefaultAsync(w => w.Id == idWell, token).ConfigureAwait(false);
            var dto = entity.Adapt<WellDto>();
            return dto.Caption;
        }

        public async Task<IEnumerable<CompanyDto>> GetCompaniesAsync(int idWell, CancellationToken token)
        {
            var well = await db.Wells
                .Include(w => w.RelationCompaniesWells)
                .ThenInclude(r => r.Company)
                .FirstOrDefaultAsync(w => w.Id == idWell, token)
                .ConfigureAwait(false);
            var companies = well.RelationCompaniesWells.Select(r => r.Company);
            return companies.Adapt<CompanyDto>();
        }
    }
}