using AsbCloudApp.Data.ProcessMap;
using AsbCloudApp.Services;
using ClosedXML.Excel;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Services.ProcessMap
{
#nullable enable
    public class ProcessMapReportMakerService : IProcessMapReportMakerService
    {
        const int firstColumn = 2;
        const int lastColumn = 49;

        const int headerRowsCount = 5;

        private readonly IProcessMapReportService processMapService;

        public ProcessMapReportMakerService(IProcessMapReportService processMapService)
        {
            this.processMapService = processMapService;
        }

        public async Task<Stream> MakeReportAsync(int idWell, CancellationToken token)
        {
            var stream = GetExcelTemplateStream();
            using var workbook = new XLWorkbook(stream, XLEventTracking.Disabled);

            var data = await processMapService.GetProcessMapReportAsync(idWell, token);

            FillProcessMapToWorkbook(workbook, data);

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

        private static void FillProcessMapToWorkbook(XLWorkbook workbook, IEnumerable<ProcessMapReportDto> data)
        {
            var sheet = workbook.Worksheets.FirstOrDefault();
            if (sheet is null)
                return;
            var dataBySections = data.GroupBy(p => p.IdWellSectionType);
            FillSheet(sheet, dataBySections);
        }

        private static void FillSheet(IXLWorksheet sheet, IEnumerable<IGrouping<int, ProcessMapReportDto>> dataBySections)
        {
            var startRow = headerRowsCount + 1;
            foreach (var sectionData in dataBySections)
            {
                if(sectionData.Any())
                    startRow = FillSection(sheet, sectionData, startRow);
            }
        }

        private static int FillSection(IXLWorksheet sheet, IGrouping<int, ProcessMapReportDto> sectionData, int row)
        {
            var rowStart = row;

            var sectionName = sectionData.FirstOrDefault()?.WellSectionTypeName 
                ?? sectionData.Key.ToString();

            sheet.Range(row, firstColumn, row, lastColumn)
                .Merge()
                .FirstCell()
                .SetVal(sectionName)
                .Style
                .Fill.SetBackgroundColor(XLColor.LightGray);

            row++;

            foreach (var interval in sectionData)
                row = FillIntervalData(sheet, interval, row);

            var sectionStyle = sheet.Range(rowStart, firstColumn, row - 1, lastColumn).Style;
            SetBorders(sectionStyle);
            return row;
        }

        private static int FillIntervalData(IXLWorksheet sheet, ProcessMapReportDto interval, int row)
        {
            const int columnDepth = firstColumn + 1;
            const int columnDate = firstColumn + 2;
            const int columnRopTime = firstColumn + 3;
            const int columnMode = firstColumn + 4;

            sheet.Cell(row, firstColumn)
              .SetVal(interval.DepthStart, "0.0");

            sheet.Cell(row, columnDepth)
               .SetVal(interval.DepthEnd, "0.0");

            sheet.Cell(row, columnDate)
                .SetVal(interval.DateStart);

            sheet.Cell(row, columnRopTime)
                .SetVal(interval.MechDrillingHours);

            row = FillIntervalModeData(sheet, interval, columnMode, row);

            return row;
        }

        private static int FillIntervalModeData(IXLWorksheet sheet, ProcessMapReportDto modeData, int column, int row)
        {
            int columnDeltaDepth = column + 1;
            int columnPressure = columnDeltaDepth + 1;
            int columnLoad = columnPressure + 5;
            int columnTorque = columnLoad + 5;
            int columnSpeed = columnTorque + 5;
            int columnUsagePlan = columnSpeed + 5;
            int columnUsageFact = columnUsagePlan + 1;
            int columnRop = columnUsageFact + 12;

            sheet.Cell(row, column)
                .SetVal(modeData.DrillingMode);

            sheet.Cell(row, columnDeltaDepth)
                .SetVal(modeData.DeltaDepth);

            FillIntervalModeDataParam(sheet, modeData.PressureDiff, columnPressure, row);
            FillIntervalModeDataParam(sheet, modeData.AxialLoad, columnLoad, row);
            FillIntervalModeDataParam(sheet, modeData.TopDriveTorque, columnTorque, row);
            FillIntervalModeDataSpeed(sheet, modeData.SpeedLimit, columnSpeed, row);

            sheet.Cell(row, columnUsagePlan)
               .SetVal(modeData.UsagePlan);

            sheet.Cell(row, columnUsageFact)
                .SetVal(modeData.UsageFact);

            sheet.Cell(row, columnRop)
                .SetVal(modeData.Rop);

            return row + 1;
        }

        private static void FillIntervalModeDataParam(IXLWorksheet sheet, ProcessMapReportParamsDto dataParam, int column, int row)
        {
            const int columnOffsetSpPlan = 0;
            const int columnOffsetSpFact = 1;
            const int columnOffsetFact = 2;
            const int columnOffsetLimit = 3;
            const int columnOffsetPercent = 4;

            sheet.Cell(row, column + columnOffsetSpPlan)
                .SetVal(dataParam.SetpointPlan);
            
            sheet.Cell(row, column + columnOffsetSpFact)
                .SetVal(dataParam.SetpointFact);

            sheet.Cell(row, column + columnOffsetFact)
                .SetVal(dataParam.Fact);

            sheet.Cell(row, column + columnOffsetLimit)
                .SetVal(dataParam.Limit);

            sheet.Cell(row, column + columnOffsetPercent)
                .SetVal(dataParam.SetpointUsage);
        }

        private static void FillIntervalModeDataSpeed(IXLWorksheet sheet, ProcessMapReportParamsDto dataParam, int column, int row)
        {
            const int columnOffsetSpPlan = 0;
            const int columnOffsetSpFact = 1;
            const int columnOffsetFact = 2;
            const int columnOffsetLimit = 3;
            const int columnOffsetPercent = 4;

            sheet.Cell(row, column + columnOffsetSpPlan)
                .SetVal(dataParam.SetpointPlan);

            sheet.Cell(row, column + columnOffsetSpFact)
                .SetVal(dataParam.SetpointFact);

            sheet.Cell(row, column + columnOffsetFact)
                .SetVal(dataParam.Fact);

            sheet.Cell(row, column + columnOffsetLimit)
                .SetVal(dataParam.Limit);

            sheet.Cell(row, column + columnOffsetPercent)
                .SetVal(dataParam.SetpointUsage);
        }

        private static Stream GetExcelTemplateStream()
        {
            var stream = System.Reflection.Assembly.GetExecutingAssembly()
                .GetManifestResourceStream("AsbCloudInfrastructure.Services.ProcessMap.ProcessMapReportTemplate.xlsx");
            return stream!;
        }

        private static IXLStyle SetBorders(IXLStyle style)
        {
            style.Border.RightBorder = XLBorderStyleValues.Thin ;
            style.Border.LeftBorder = XLBorderStyleValues.Thin;
            style.Border.TopBorder = XLBorderStyleValues.Thin ;
            style.Border.BottomBorder = XLBorderStyleValues.Thin ;
            style.Border.InsideBorder = XLBorderStyleValues.Thin ;
            return style;
        }
    }
#nullable disable
}