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

namespace AsbCloudInfrastructure.Services.DailyReport
{
#nullable enable
    public class DailyReportService : IDailyReportService
    {
        private readonly IAsbCloudDbContext db;
        private readonly IWellService wellService;
        private readonly DailyReportMakerExcel dailyReportMaker = new DailyReportMakerExcel();

        public DailyReportService(IAsbCloudDbContext db, IWellService wellService)
        {
            this.db = db;
            this.wellService = wellService;
        }

        public async Task<IEnumerable<DailyReportDto>> GetListAsync(int idWell, DateTime? begin, DateTime? end, CancellationToken token)
        {            
            var query = db.DailyReports.Where(r => r.IdWell == idWell);
            var offsetHours = wellService.GetTimezone(idWell).Hours;

            if (begin is not null)
            {
                var beginOffset = ((DateTime)begin).ToUtcDateTimeOffset(offsetHours);
                query = query.Where(d => d.StartDate >= beginOffset);
            }

            if (end is not null)
            {
                var endOffset = ((DateTime)end).ToUtcDateTimeOffset(offsetHours);
                query = query.Where(d => d.StartDate <= endOffset);
            }
            
            var entities = await query
                .ToListAsync(token);

            return entities.Select(r => Convert(r, offsetHours));
        }

        public async Task<DailyReportDto> GetOrGenerateAsync(int idWell, DateTime date, CancellationToken token)
        {
            var dailyReportDto = await GetAsync(idWell, date, token);
            if (dailyReportDto is null)
                return await MakeDefaultDailyReportAsync(idWell, date, token);
            else
                return dailyReportDto;
        }

        public async Task<int> AddAsync(int idWell, DailyReportDto dto, CancellationToken token = default)
        {
            var offsetHours = wellService.GetTimezone(idWell).Hours;
            var reportDateOffset = dto.ReportDate.ToUtcDateTimeOffset(offsetHours);
            var info = Convert(dto, offsetHours);
            var entity = new AsbCloudDb.Model.DailyReport
            {
                IdWell = idWell,
                StartDate = reportDateOffset,
                Info = info,
            };
            db.DailyReports.Add(entity);
            var result = await db.SaveChangesAsync(token);
            return result;
        }

        public async Task<int> UpdateAsync(int idWell, DateTime date, DailyReportDto dto, CancellationToken token)
        {
            var offsetHours = wellService.GetTimezone(idWell).Hours;
            var dateOffset = date.ToUtcDateTimeOffset(offsetHours);
            var entity = await db.DailyReports
                .FirstOrDefaultAsync(r => r.IdWell == idWell &&
                    r.StartDate.Year == dateOffset.Year &&
                    r.StartDate.DayOfYear == dateOffset.DayOfYear
                    , token);

            if (entity is null)
                return 0;

            entity.Info = Convert(dto, offsetHours);
            db.DailyReports.Update(entity);

            var result = await db.SaveChangesAsync(token);
            return result;
        }

        public async Task<Stream?> MakeReportAsync(int idWell, DateTime date, CancellationToken token = default)
        {
            var dailyReportDto = await GetAsync(idWell, date, token);
            if (dailyReportDto is null)
                return null;

            var memoryStream = dailyReportMaker.MakeReport(dailyReportDto);
            return memoryStream;
        }

        private async Task<DailyReportDto?> GetAsync(int idWell, DateTime date, CancellationToken token)
        {
            var offsetHours = wellService.GetTimezone(idWell).Hours;
            var dateOffset = date.ToUtcDateTimeOffset(offsetHours);
            var entity = await db.DailyReports
                .FirstOrDefaultAsync(r => r.IdWell == idWell &&
                    r.StartDate.Year == dateOffset.Year &&
                    r.StartDate.DayOfYear == dateOffset.DayOfYear
                    , token);

            if (entity is null)
                return null;
            else
                return Convert(entity, offsetHours);
        }

        private async Task<DailyReportDto> MakeDefaultDailyReportAsync(int idWell, DateTime date, CancellationToken token)
        {
            var well = await wellService.GetAsync(idWell, token);
            var offsetHours = wellService.GetTimezone(idWell).Hours;
            var dto = new DailyReportDto()
            {
                ReportDate = date,
                WellName = well.Caption,
                ClusterName = well.Cluster,                
            };
            DailyReportDto result = dto;
            return result;
        }

        private static DailyReportDto Convert(AsbCloudDb.Model.DailyReport entity, double offsetHours)
        {
            var dto = entity.Info.Adapt<DailyReportDto>();
            dto.ReportDate = entity.StartDate
                .ToRemoteDateTime(offsetHours);
            return dto;
        }

        private static DailyReportInfo Convert(DailyReportDto dto, double offsetHours)
        {
            var entity = dto.Adapt<DailyReportInfo>();            
            entity.ReportDate = dto.ReportDate
                .ToUtcDateTimeOffset(offsetHours)
                .Date;
            return entity;
        }
    }
#nullable disable
}