using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Repository;
using AsbSaubReport;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Services
{
    public class ReportService : IReportService
    {
        private readonly IAsbCloudDbContext db;
        private readonly string connectionString;
        private readonly ITelemetryService telemetryService;
        private readonly IWellService wellService;
        private readonly IBackgroundWorkerService backgroundWorkerService;

        public ReportService(IAsbCloudDbContext db, IConfiguration configuration,
            ITelemetryService telemetryService, IWellService wellService, IBackgroundWorkerService backgroundWorkerService)
        {
            this.db = db;
            this.connectionString = configuration.GetConnectionString("DefaultConnection");
            this.wellService = wellService;
            this.backgroundWorkerService = backgroundWorkerService;
            this.telemetryService = telemetryService;
            ReportCategoryId = db.FileCategories.AsNoTracking()
                .FirstOrDefault(c =>
                c.Name.Equals("Рапорт")).Id;
        }

        public int ReportCategoryId { get; private set; }

        public string CreateReport(int idWell, int idUser, int stepSeconds, int format, DateTime begin,
            DateTime end, Action<object, string> progressHandler)
        {
            var timezoneOffset = wellService.GetTimezone(idWell).Hours;
            var beginUtc = begin.ToUtcDateTimeOffset(timezoneOffset);
            var endUtc = end.ToUtcDateTimeOffset(timezoneOffset);
            var beginRemote = begin.ToTimeZoneOffsetHours(timezoneOffset);
            var endRemote = end.ToTimeZoneOffsetHours(timezoneOffset);

            var newReportId = backgroundWorkerService.Enqueue(async (id, token) =>
            {
                var contextOptions = new DbContextOptionsBuilder<AsbCloudDbContext>()
                    .UseNpgsql(connectionString)
                    .Options;
                using var context = new AsbCloudDbContext(contextOptions);

                var tempDir = Path.Combine(Path.GetTempPath(), "report");

                var generator = GetReportGenerator(idWell, beginRemote, endRemote, stepSeconds, format, context);
                var reportFileName = Path.Combine(tempDir, generator.GetReportDefaultFileName());
                var totalPages = generator.GetPagesCount();

                generator.OnProgress += (s, e) =>
                {
                    progressHandler.Invoke(e.Adapt<ReportProgressDto>(), id);
                };
                generator.Make(reportFileName);

                var fileRepository = new FileRepository(context);
                var fileStorageRepository = new FileStorageRepository();
                var fileService = new FileService(fileRepository, fileStorageRepository);
                var fileInfo = await fileService.MoveAsync(idWell, idUser, ReportCategoryId, reportFileName, reportFileName, token);

                progressHandler.Invoke(new
                {
                    Operation = "done",
                    Progress = 100f,
                    TotalPages = totalPages,
                    CurrentPage = totalPages,
                    file = fileInfo,
                }, id);

                var newReportProperties = new ReportProperty
                {
                    IdWell = idWell,
                    IdFile = fileInfo.Id,
                    Begin = beginUtc,
                    End = endUtc,
                    Step = stepSeconds,
                    Format = format
                };
                context.ReportProperties.Add(newReportProperties);
                context.SaveChanges();
            });
            progressHandler.Invoke(new ReportProgressDto
            {
                Operation = "Ожидает начала в очереди.",
                Progress = 0f,
            }, newReportId);
            return newReportId;
        }

        public int GetReportPagesCount(int idWell, DateTime begin, DateTime end, int stepSeconds, int format)
        {
            var timezoneOffset = wellService.GetTimezone(idWell).Hours;
            var beginRemote = begin.ToTimeZoneOffsetHours(timezoneOffset);
            var endRemote = end.ToTimeZoneOffsetHours(timezoneOffset);

            var generator = GetReportGenerator(idWell, beginRemote, endRemote, stepSeconds, format, (AsbCloudDbContext)db);
            var pagesCount = generator.GetPagesCount();
            return pagesCount;
        }

        public DatesRangeDto GetDatesRangeOrDefault(int idWell)
        {
            var idTelemetry = telemetryService.GetIdTelemetryByIdWell(idWell);
            if (idTelemetry is null)
                return null;
            var range = telemetryService.GetDatesRange((int)idTelemetry);
            return range;
        }

        public async Task<IEnumerable<ReportPropertiesDto>> GetAllReportsByWellAsync(int idWell, CancellationToken token)
        {
            var timezoneOffset = wellService.GetTimezone(idWell).Hours;
            var propertiesQuery = db.ReportProperties.Include(r => r.File)
             .Where(p => p.IdWell == idWell)
            .OrderBy(o => o.File.UploadDate)
            .AsNoTracking()
            .Take(1024);
            var properties = await propertiesQuery.ToListAsync(token);
            return properties.Select(p => new ReportPropertiesDto
            {
                Id = p.Id,
                Name = p.File.Name,
                File = new FileInfoDto
                {
                    Id = p.File.Id,
                    Author = null,
                    IdAuthor = p.File.IdAuthor ?? 0,
                    IdCategory = p.File.IdCategory,
                    IdWell = p.File.IdWell,
                    Name = p.File.Name,
                    Size = p.File.Size,
                    UploadDate = p.File.UploadDate.ToRemoteDateTime(timezoneOffset),
                },
                IdWell = p.IdWell,
                Date = p.File.UploadDate.ToRemoteDateTime(timezoneOffset),
                Begin = p.Begin.ToRemoteDateTime(timezoneOffset),
                End = p.End.ToRemoteDateTime(timezoneOffset),
                Step = p.Step,
                Format = p.Format == 0 ? ".pdf" : ".las"
            });
        }

        private static IReportGenerator GetReportGenerator(int idWell, DateTime begin,
            DateTime end, int stepSeconds, int format, AsbCloudDbContext context)
        {
            var dataSource = new ReportDataSourcePgCloud(context, idWell);
            IReportGenerator generator = format switch
            {
                //LAS
                1 => new AsbSaubReportLas.ReprotGeneratorLas(dataSource),
                //PDF
                _ => new AsbSaubReportPdf.ReprotGeneratorPdf(dataSource),
            };
            generator.Begin = begin;
            generator.End = end;
            generator.Step = TimeSpan.FromSeconds(stepSeconds);
            generator.WithCharts = true;
            generator.WithEvents = true;

            return generator;
        }
    }
}