using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbSaubReport;
using AsbSaubReportPdf;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace AsbCloudInfrastructure.Services
{
    public class ReportService : IReportService
    {
        private readonly IAsbCloudDbContext db;
        private readonly IConfiguration configuration;
        private readonly ITelemetryService telemetryService;
        private readonly IFileService fileService;
        private readonly IBackgroundQueue queue;

        public ReportService(IAsbCloudDbContext db, IConfiguration configuration,
            ITelemetryService telemetryService, IFileService fileService,
            IBackgroundQueue queue)
        {
            this.db = db;
            this.configuration = configuration;
            this.telemetryService = telemetryService;
            this.fileService = fileService;
            this.queue = queue;
            ReportCategoryId = db.FileCategories.FirstOrDefault(c =>
                c.Name.Equals("Рапорт")).Id;
        }

        public int ReportCategoryId { get; private set; }

        public int CreateReport(int idWell, int idUser, int stepSeconds, int format, DateTime begin,
            DateTime end, Action<float, string, int> progressHandler, Action<string, int> reportNameHandler)
        {
            var newReportId = queue.EnqueueTask((id) =>
            {
                var optionsBuilder = new DbContextOptionsBuilder<AsbCloudDbContext>();
                optionsBuilder.UseNpgsql(configuration.GetConnectionString("DefaultConnection"));

                using (var context = new AsbCloudDbContext(optionsBuilder.Options))
                {
                    var generator = GetReportGenerator(idWell, begin, end, stepSeconds, format, context);
                    generator.OnProgress += (s, e) => progressHandler.Invoke(e.progress, e.operation, id);
                    var newReportName = generator.Make();
                    if (newReportName is not null)
                    {
                        var shorReportName = Path.GetFileName(newReportName);
                        reportNameHandler.Invoke(shorReportName, id);

                        var newReportFile = new AsbCloudDb.Model.File
                        {
                            IdWell = idWell,
                            IdAuthor = idUser,
                            IdCategory = ReportCategoryId,
                            Name = newReportName,
                            Date = DateTime.Now,
                        };

                        context.Files.Add(newReportFile);
                        context.SaveChanges();

                        var newReportProperties = new ReportProperties
                        {
                            IdWell = idWell,
                            IdFile = newReportFile.Id,
                            Begin = begin,
                            End = end,
                            Step = stepSeconds,
                            Format = format
                        };
                        context.ReportProperties.Add(newReportProperties);
                        context.SaveChanges();
                    }
                }
            });
            return newReportId;
        }

        public int GetReportPagesCount(int idWell, DateTime begin, DateTime end, int stepSeconds, int format)
        {
            var generator = GetReportGenerator(idWell, begin, end, stepSeconds, format, (AsbCloudDbContext)db);

            return generator.GetPagesCount();
        }

        public IEnumerable<ReportPropertiesDto> GetSuitableReports(int idWell, DateTime begin, DateTime end, int stepSeconds, int format)
        {
            var suitableReportsFromDb = GetSuitableReportsFromDb(idWell, begin, end, stepSeconds, format);

            var suitableReportsProperties = suitableReportsFromDb.Select(r => new ReportPropertiesDto
            {
                Id = r.Id,
                Name = Path.GetFileName(r.File.Name),
                FullName = r.File.Name,
                IdWell = r.IdWell,
                Date = r.File.Date,
                Begin = r.Begin,
                End = r.End,
                Step = r.Step,
                Format = r.Format == 0 ? ".pdf" : ".las"
            });

            return suitableReportsProperties;
        }

        public DatesRangeDto GetReportsDatesRange(int idWell)
        {
            var telemetry = telemetryService.GetTelemetryByidWell(idWell);

            if (telemetry is null)
                return null;

            var datesRange = (from d in db.DataSaubBases
                              where d.IdTelemetry == telemetry.Id
                              select d.Date).Union(
                                from m in db.Messages
                                where m.IdTelemetry == telemetry.Id
                                select m.Date).DefaultIfEmpty().GroupBy(g => true)
                                .Select(g => new
                                {
                                    From = g.Min(),
                                    To = g.Max()
                                }).OrderBy(gr => gr.From).FirstOrDefault();

            return new DatesRangeDto
            {
                From = datesRange.From,
                To = datesRange.To.Year == 1 ? DateTime.MaxValue : datesRange.To
            };
        }

        private IEnumerable<ReportProperties> GetSuitableReportsFromDb(int idWell, DateTime begin, DateTime end, int stepSeconds, int format)
        {
            var suitableReportsNames = (from r in db.ReportProperties.Include(r => r.File)
                                        where r.IdWell == idWell
                                        && r.Begin >= begin
                                        && r.End <= end
                                        && r.Step <= stepSeconds
                                        && r.Format == format
                                        select r).OrderBy(o => o.File.Date)
                                        .Take(512).ToList();

            return suitableReportsNames;
        }

        private IReportGenerator GetReportGenerator(int idWell, DateTime begin, DateTime end, int stepSeconds, int format, AsbCloudDbContext context)
        {
            var dataSource = new ReportDataSourcePgCloud(context, idWell);

            IReportGenerator generator;
            switch (format)
            {
                case 1: //LAS
                    generator = new AsbSaubReportLas.LasReprotGenerator(dataSource);
                    break;
                case 0: //PDF
                default:
                    generator = new PdfReprotGenerator(dataSource);
                    break;
            }

            generator.ReportDirectory = Path.Combine(fileService.RootPath, $"{idWell}", $"{ReportCategoryId}");
            generator.Begin = begin;
            generator.End = end;
            generator.Step = TimeSpan.FromSeconds(stepSeconds);
            generator.WithCharts = true;
            generator.WithEvents = true;

            return generator;
        }
    }
}