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

namespace AsbCloudInfrastructure.Services.DetectOperations
{
    internal class DetectedOperationExportService
    {
        private readonly IAsbCloudDbContext db;
        private readonly IWellService wellService;

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

        public async Task<Stream> ExportAsync(IEnumerable<int> idsWells, CancellationToken token)
        {
            using var workbook = new XLWorkbook(XLEventTracking.Disabled);

            await AddSheetsAsync(workbook, idsWells, token);

            MemoryStream memoryStream = new MemoryStream();
            workbook.SaveAs(memoryStream, new SaveOptions { });
            memoryStream.Seek(0, SeekOrigin.Begin);
            return memoryStream;
        }

        private async Task AddSheetsAsync(XLWorkbook workbook, IEnumerable<int> idsWells, CancellationToken token)
        {
            if(!idsWells.Any())
                return;

            var wells = idsWells.Select(i => wellService.GetOrDefault(i))
                .Where(w => w is not null && w.IdTelemetry is not null);

            if (!wells.Any())
                return;

            var idsTelemetries = wells.Select(w => w.IdTelemetry);
            if (!idsTelemetries.Any())
                return;

            var operations = await db.DetectedOperations
                .Include(o => o.OperationCategory)
                .AsNoTracking()
                .Where(o => idsTelemetries.Contains(o.IdTelemetry))
                .OrderBy(o => o.IdTelemetry)
                .ThenBy(o => o.DateStart)
                .ToListAsync(token);

            var groups = operations.GroupBy(o => o.IdTelemetry);

            foreach (var well in wells)
            {
                var ops = groups.FirstOrDefault(g => g.Key == well.IdTelemetry)
                    ?.ToList();
                
                if(ops?.Any() != true)
                    continue;

                var sheetName = $"{well.Cluster}_{well.Caption}"
                    .Replace('.','_');

                var sheet = workbook.AddWorksheet(sheetName);
                AddHeader(sheet);
                const int headerHeight = 1;
                for(var i = 0; i< ops.Count; i++ )
                    AddRow(sheet, ops[i], well, i + 1 + headerHeight);
            }
        }

        private static void AddHeader(IXLWorksheet sheet)
        {
            var rowNumber = 1; 
            sheet.Cell(rowNumber, 1).Value = "Name";
            sheet.Column(1).Width = 34;
            
            sheet.Cell(rowNumber, 2).Value = "DateStart";
            sheet.Column(2).Width = 17;
            
            sheet.Cell(rowNumber, 3).Value = "DateEnd";
            sheet.Column(3).Width = 17;
            
            sheet.Cell(rowNumber, 4).Value = "DepthStart";
            sheet.Column(4).Width = 9;
            
            sheet.Cell(rowNumber, 5).Value = "DepthEnd";
            sheet.Column(5).Width = 9;
            
            sheet.Cell(rowNumber, 6).Value = "KeyValue";
            sheet.Column(6).Width = 9;

            sheet.SheetView.FreezeRows(rowNumber);
        }

        private static void AddRow(IXLWorksheet sheet, DetectedOperation operation, WellDto well,  int rowNumber)
        {
            var timezoneoffsetHours = well.Timezone.Hours;
            sheet.Cell(rowNumber, 1).Value = operation.OperationCategory.Name;
            sheet.Cell(rowNumber, 2).Value = operation.DateStart.ToRemoteDateTime(timezoneoffsetHours);
            sheet.Cell(rowNumber, 3).Value = operation.DateEnd.ToRemoteDateTime(timezoneoffsetHours);
            sheet.Cell(rowNumber, 4).Value = operation.DepthStart;
            sheet.Cell(rowNumber, 5).Value = operation.DepthEnd;
            sheet.Cell(rowNumber, 6).Value = operation.Value;
        }
    }
}