using AsbCloudApp.Data;
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Repositories;
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.Repository
{

    public class TelemetryWirelineRunOutRepository : ITelemetryWirelineRunOutRepository
    {
        private readonly IAsbCloudDbContext context;
        private readonly ITelemetryService telemetryService;
        private readonly IWellService wellService;

        public TelemetryWirelineRunOutRepository(IAsbCloudDbContext context, 
            ITelemetryService telemetryService,
            IWellService wellService)
        {
            this.context = context;
            this.telemetryService = telemetryService;
            this.wellService = wellService;
        }

        /// <inheritdoc/>
        public async Task<int> AddOrUpdateAsync(string uid, TelemetryWirelineRunOutBaseDto dto, CancellationToken token)
        {
            var idTelemetry = telemetryService.GetOrCreateTelemetryByUid(uid).Id;
            var timezoneOffset = telemetryService.GetTimezone(idTelemetry).Hours;
            var entity = Convert(idTelemetry, dto, timezoneOffset);
            
            if (await context.TelemetryWirelineRunOut.AnyAsync(w => w.IdTelemetry == idTelemetry, token))
                context.TelemetryWirelineRunOut.Update(entity);
            else
                context.TelemetryWirelineRunOut.Add(entity);

            return await context.SaveChangesAsync(token);
        }

        /// <inheritdoc/>
        public async Task<TelemetryWirelineRunOutDto?> GetOrDefaultAsync(int idWell, CancellationToken token)
        {
            var well = await wellService.GetOrDefaultAsync(idWell, token).ConfigureAwait(false);
            if (well is null)
                return null;

            return await GetOrDefaultAsync(well, token);
        }

        private async Task<TelemetryWirelineRunOutDto?> GetOrDefaultAsync(WellDto well, CancellationToken token)
        {
            var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(well.Id);
            if (telemetry is null)
                return null;

            var entity = await context.TelemetryWirelineRunOut
                .Where(x => x.IdTelemetry == telemetry.Id)
                .AsNoTracking()
                .FirstOrDefaultAsync(token)
                .ConfigureAwait(false);

            if (entity is null)
                return null;

            var timezoneHours = well.Timezone.Hours;
            return Convert(entity, well, timezoneHours);
        }

        /// <inheritdoc/>
        public async Task<IEnumerable<TelemetryWirelineRunOutDto>> GetAllAsync(int idCompany, CancellationToken token)
        {
            var wells = await wellService.GetAsync(new() { IdCompany = idCompany }, token)
                .ConfigureAwait(false);

            var result = new List<TelemetryWirelineRunOutDto>(wells.Count());
            foreach (var well in wells)
            {
                var dto = await GetOrDefaultAsync(well, token);
                if (dto is not null)
                    result.Add(dto);
            }

            return result;
        }

        private static TelemetryWirelineRunOut Convert(int idTelemetry, TelemetryWirelineRunOutBaseDto dto, double timezoneOffset)
        {
            var entity = dto.Adapt<TelemetryWirelineRunOut>();
            entity.IdTelemetry = idTelemetry;
            entity.DateTime = dto.DateTime.ToUtcDateTimeOffset(timezoneOffset);
            return entity;
        }

        private static TelemetryWirelineRunOutDto Convert(TelemetryWirelineRunOut entity, WellDto well, double timezoneOffset)
        {
            var dto = entity.Adapt<TelemetryWirelineRunOutDto>();
            dto.DateTime = entity.DateTime.ToRemoteDateTime(timezoneOffset);
            dto.WellInfo = well;
            return dto;
        }
    }

}