using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SWRIS.Core;
using SWRIS.Dtos;
using SWRIS.Models;
using System;
using System.Collections.Generic;
using System.Linq;

namespace SWRIS.Extensions
{
    public class ReportPdfExporter
    {
        private readonly string[] ChineseNumbers = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" };
        private readonly string _crane;
        private readonly RecordDto _record;
        private readonly EquipmentModel _equipment;
        private readonly int _damageNearPointCount = 20;
        public ReportPdfExporter(string crane, RecordDto record, EquipmentModel equipment)
        {
            _crane = crane;
            _record = record;
            _equipment = equipment;
            _damageNearPointCount = App.Config.DamageNearPointCount;
        }
        public bool SavePdf(string filePath)
        {
            if (_record == null || _equipment == null)
            {
                return false;
            }
            Document.Create(document =>
            {
                var damageList = _record.Damages.OrderByDescending(c => c.DamageValue).Take(5).OrderBy(c => c.DamagePoint).ToList();
                List<(double? position, double? damage, byte[] image)> images = GenerateImage(_record.DataFilePath,
                                                                                            _record.SensorCount,
                                                                                            _record.StartPoint,
                                                                                            _record.EndPoint,
                                                                                            damageList: damageList);
                document.Page(page =>
                {
                    page.Size(PageSizes.A4);
                    page.MarginTop(2, Unit.Centimetre);
                    page.MarginBottom(1, Unit.Centimetre);
                    page.MarginLeft(1, Unit.Centimetre);
                    page.MarginRight(1, Unit.Centimetre);
                    page.Header().AlignCenter().AlignTop().Text("钢丝绳无损检测报告").FontSize(20).Bold();
                    page.Content()
                        .AlignCenter()
                        .AlignTop()
                        .PaddingTop(20)
                        .Table(table =>
                        {
                            table.ColumnsDefinition(columns =>
                            {
                                columns.ConstantColumn(22);
                                columns.RelativeColumn();
                                columns.RelativeColumn();
                                columns.RelativeColumn();
                                columns.RelativeColumn();
                                columns.RelativeColumn();
                                columns.RelativeColumn();
                            });
                            table.Cell().Row(1).Column(1).ColumnSpan(4).PaddingBottom(5).Text($"所在起重机：{_crane}").AlignLeft();
                            table.Cell().Row(1).Column(5).ColumnSpan(3).PaddingBottom(5).Text($"检测时间：{_record.StartTime:yyyy-MM-dd HH:mm:ss}")
                                 .AlignRight();

                            table.Cell().Row(2).Column(1).RowSpan(7).Border(1).AlignMiddle().Text("基本信息").Bold().AlignCenter();

                            table.Cell().Row(2).Column(2).Element(CellNameStyle).Text("被检测钢丝绳");
                            table.Cell().Row(2).Column(3).ColumnSpan(2).Element(CellValueStyle).Text(_equipment.RopeName);
                            table.Cell().Row(2).Column(5).Element(CellNameStyle).Text("钢丝绳制造商");
                            table.Cell().Row(2).Column(6).ColumnSpan(2).Element(CellValueStyle).Text(_equipment.Supplier);

                            table.Cell().Row(3).Column(2).Element(CellNameStyle).Text("钢丝绳长");
                            table.Cell().Row(3).Column(3).ColumnSpan(2).Element(CellValueStyle).Text(_equipment.Parameter.WireRopeLength.ToString() + "m");
                            table.Cell().Row(3).Column(5).Element(CellNameStyle).Text("检测长度");
                            table.Cell().Row(3).Column(6).ColumnSpan(2).Element(CellValueStyle).Text(_record.DetectionLength.ToString("F3") + "m");

                            table.Cell().Row(4).Column(2).Element(CellNameStyle).Text("开始位置");
                            table.Cell().Row(4).Column(3).ColumnSpan(2).Element(CellValueStyle).Text(_record.StartPoint.ToString("F3") + "m");
                            table.Cell().Row(4).Column(5).Element(CellNameStyle).Text("结束位置");
                            table.Cell().Row(4).Column(6).ColumnSpan(2).Element(CellValueStyle).Text(_record.EndPoint.ToString("F3") + "m");

                            table.Cell().Row(5).Column(2).Element(CellNameStyle).Text("钢丝绳规格");
                            table.Cell().Row(5).Column(3).ColumnSpan(2).Element(CellValueStyle).Text(
                                 $"{_equipment.Parameter.WireRopeDiameter} {_equipment.WireSurfaceType} " +
                                 $"{_equipment.Parameter.WireRopeStrandCount}*" +
                                 $"{_equipment.Parameter.WireRopeStrandWireCount}S+{_equipment.RopeCoreType} " +
                                 $"{_equipment.LayType}");
                            table.Cell().Row(5).Column(5).Element(CellNameStyle).Text("钢丝材质");
                            table.Cell().Row(5).Column(6).ColumnSpan(2).Element(CellValueStyle).Text(_equipment.WireMaterialType.GetDescription());

                            table.Cell().Row(6).Column(2).Element(CellNameStyle).Text("钢丝绳公称直径");
                            table.Cell().Row(6).Column(3).ColumnSpan(2).Element(CellValueStyle).Text(_equipment.Parameter.WireRopeDiameter + "mm");
                            table.Cell().Row(6).Column(5).Element(CellNameStyle).Text("绳芯材质");
                            table.Cell().Row(6).Column(6).ColumnSpan(2).Element(CellValueStyle).Text(_equipment.RopeCoreType.GetDescription());

                            table.Cell().Row(7).Column(2).Element(CellNameStyle).Text("捻制方法");
                            table.Cell().Row(7).Column(3).ColumnSpan(2).Element(CellValueStyle).Text(_equipment.LayType.GetDescription());
                            table.Cell().Row(7).Column(5).Element(CellNameStyle).Text("钢丝绳表面状态");
                            table.Cell().Row(7).Column(6).ColumnSpan(2).Element(CellValueStyle).Text(_equipment.WireSurfaceType.GetDescription());

                            table.Cell().Row(8).Column(2).Element(CellNameStyle).Text("依据标准");
                            table.Cell().Row(8).Column(3).ColumnSpan(5).Element(CellValueStyle).AlignLeft().Text(
                                "GB/T 21837-2023 《铁磁性钢丝绳电磁检测方法》\r\n" +
                                "GB/T 26832-2011 《无损检测仪器 钢丝绳电磁检测仪技术条件》\r\n" +
                                "GB/T 5972-2023  《起重机 钢丝绳 保养、维护、检验和报废》");

                            table.Cell().Row(9).Column(1).RowSpan(5).Border(1).AlignMiddle().Text("检测结果").Bold().AlignCenter();

                            table.Cell().Row(9).RowSpan(2).Column(2).Element(CellNameStyle).Text("最大损伤\r\n程度");
                            table.Cell().Row(9).Column(3).ColumnSpan(5).Element(CellNameStyle).Text("五处重要损伤");

                            table.Cell().Row(10).Column(3).Element(CellValueStyle).Text("第一处");
                            table.Cell().Row(10).Column(4).Element(CellValueStyle).Text("第二处");
                            table.Cell().Row(10).Column(5).Element(CellValueStyle).Text("第三处");
                            table.Cell().Row(10).Column(6).Element(CellValueStyle).Text("第四处");
                            table.Cell().Row(10).Column(7).Element(CellValueStyle).Text("第五处");

                            table.Cell().Row(11).Column(2).Element(CellNameStyle).Text("损伤位置");
                            table.Cell().Row(12).Column(2).Element(CellNameStyle).Text("损伤当量");
                            table.Cell().Row(13).Column(2).Element(CellNameStyle).Text("损伤级别");

                            for (int i = 0; i < 5; i++)
                            {
                                string positionValue = i < damageList.Count ? damageList[i].DamagePoint.ToString("F3") + "m" : "-";
                                string damageValue = i < damageList.Count ? damageList[i].DamageValue.ToString("F3") + "%" : "-";
                                string damageLevel = i < damageList.Count ? damageList[i].DamageLevel.GetDescription() : "-";
                                table.Cell().Row(11).Column((uint)(3 + i)).Element(CellValueStyle).Text(positionValue);
                                table.Cell().Row(12).Column((uint)(3 + i)).Element(CellValueStyle).Text(damageValue);
                                table.Cell().Row(13).Column((uint)(3 + i)).Element(CellValueStyle).Text(damageLevel);
                            }
                        });
                });
                document.Page(page =>
                {
                    page.Size(PageSizes.A4);
                    page.MarginTop(2, Unit.Centimetre);
                    page.MarginBottom(2, Unit.Centimetre);
                    page.MarginLeft(1, Unit.Centimetre);
                    page.MarginRight(1, Unit.Centimetre);
                    page.Content().Column(column =>
                    {
                        for (int i = 0; i < images.Count; i++)
                        {
                            if (i == 0)
                            {
                                column.Item().PaddingLeft(20).Text($"整绳检测波形图").FontSize(12).Bold();
                                column.Item().Image(images[i].image);
                                column.Item().Height(35);
                            }
                            else
                            {
                                column.Item().PaddingLeft(20).Text($"第{ChineseNumbers[i]}处重要损伤").FontSize(12).Bold();
                                column.Item().Image(images[i].image);
                                column.Item().AlignCenter().Text($"损伤位置:{images[i].position?.ToString("F3")}m  " +
                                    $"损伤量值:{images[i].damage?.ToString("F3")}%").FontSize(10).FontColor(Color.FromHex("#7a08fa"));
                                column.Item().Height(34);
                            }
                        }
                    });
                });
            }).GeneratePdf(filePath);
            return true;
        }
        protected List<(double? position, double? damage, byte[] image)> GenerateImage(string dataFilePath,
                                                                                            int sensorCount,
                                                                                            double startPoint,
                                                                                            double endPoint,
                                                                                            List<DamageDto> damageList)
        {
            List<(double? position, double? damage, byte[] image)> imageDamages = new List<(double? position, double? damage, byte[] image)>();
            if (dataFilePath.IsNullOrEmpty())
            {
                return null;
            }
            var damageData = DatFileHandler.ReadDatFile(dataFilePath);
            if (damageData.Length > 0)
            {
                (double[] positions, ushort[] damages) = ParseSensorData(damageData, sensorCount, startPoint, endPoint);
                if (positions != null && damages != null)
                {

                    ScottPlot.Plot plot = new ScottPlot.Plot();
                    plot.Add.Scatter(positions, damages, new ScottPlot.Color("#70a1d7"));
                    plot.Axes.SetLimits(startPoint * 0.95, endPoint * 1.05, 40, 80);
                    imageDamages.Add((null, null, plot.GetImageBytes(900, 300)));

                    foreach (var damage in damageList)
                    {
                        int damageIndex = FindClosestIndexBinarySearch(positions, damage.DamagePoint);
                        if (damageIndex >= 0)
                        {
                            // 计算左右各10个点的索引范围
                            int startIndex = Math.Max(0, damageIndex - _damageNearPointCount);
                            int endIndex = Math.Min(positions.Length - 1, damageIndex + _damageNearPointCount);

                            // 设置X轴范围为左右各10个点
                            double start = positions[startIndex];
                            double end = positions[endIndex];
                            plot.Axes.SetLimitsX(start, end);
                        }
                        else
                        {
                            // 如果找不到精确匹配，使用近似值
                            double start = damage.DamagePoint * 0.995;
                            double end = damage.DamagePoint * 1.005;
                            plot.Axes.SetLimitsX(start, end);
                        }

                        imageDamages.Add((damage.DamagePoint, damage.DamageValue, plot.GetImageBytes(900, 300)));
                    }
                }

            }
            return imageDamages;

        }
        public (double[] Positions, ushort[] Damages) ParseSensorData(byte[] data,
                                                                             int sensorCount,
                                                                             double startPosition,
                                                                             double endPosition)
        {
            // 检查数据长度是否能被传感器数量整除
            if (data.Length % sensorCount != 0)
            {
                LogHelper.Error($"数据长度不匹配。数据长度 {data.Length} 字节不能被传感器数量 {sensorCount} 整除。");
                return (null, null);
            }

            // 计算样本数量
            int sampleCount = data.Length / sensorCount;
            // 计算采样步长
            double samplingStep = (endPosition - startPosition) / (sampleCount - 1);

            // 初始化结果数组
            double[] positions = new double[sampleCount];
            ushort[] damages = new ushort[sampleCount];

            // 解析数据并计算平均值
            for (int sampleIndex = 0; sampleIndex < sampleCount; sampleIndex++)
            {
                positions[sampleIndex] = startPosition;
                // 计算当前样本的所有传感器Damage总和
                int sum = 0;
                for (int sensorIndex = 0; sensorIndex < sensorCount; sensorIndex++)
                {
                    // 计算数据在字节数组中的位置
                    int dataIndex = (sampleIndex * sensorCount) + sensorIndex;
                    sum += data[dataIndex];
                }
                // 计算平均值
                damages[sampleIndex] = (ushort)(sum / sensorCount);
                startPosition += samplingStep;
            }
            return (positions, damages);
        }
        /// <summary>
        /// 使用二分查找在有序数组中查找最接近目标值的索引
        /// </summary>
        /// <returns></returns>
        private int FindClosestIndexBinarySearch(double[] array, double targetValue)
        {
            if (array == null || array.Length == 0)
                return -1;

            int left = 0;
            int right = array.Length - 1;

            while (left <= right)
            {
                int mid = left + (right - left) / 2;

                if (array[mid] == targetValue)
                    return mid;
                else if (array[mid] < targetValue)
                    left = mid + 1;
                else
                    right = mid - 1;
            }

            // 此时left和right已经交叉，需要比较最接近的两个值
            if (right < 0) return 0;
            if (left >= array.Length) return array.Length - 1;

            return Math.Abs(array[left] - targetValue) < Math.Abs(array[right] - targetValue) ? left : right;
        }
        static IContainer CellNameStyle(IContainer container)
        {
            return container
                .DefaultTextStyle(x => x.FontColor(Colors.Black).Bold())
                .Border(1)
                .Padding(10)
                .AlignCenter()
                .AlignMiddle();
        }
        static IContainer CellValueStyle(IContainer container)
        {
            return container
                .DefaultTextStyle(x => x.FontColor(Colors.Black).Medium())
                .Border(1)
                .Padding(10)
                .AlignCenter()
                .AlignMiddle();
        }
    }
}
