using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace Components
{
    public partial class ComponentExcelTableWithColumnHeader : Component
    {
        public ComponentExcelTableWithColumnHeader()
        {
            InitializeComponent();
        }

        public ComponentExcelTableWithColumnHeader(IContainer container) : this()
        {
            container.Add(this);
        }

        public void GenerateExcelFile<T>(
            string filePath,
            string documentTitle,
            List<(int StartRow, int EndRow, int StartCol, int EndCol, string title)> mergeCellsInfo,
            List<T> tableData,
            List<(string title, string propertyName, float height)> headersConfig)
        {
            if (string.IsNullOrEmpty(filePath) || headersConfig == null)
            {
                throw new ArgumentException("Все входные данные должны быть заполнены.");
            }

            Excel.Application excelApp = new Excel.Application();
            excelApp.Visible = false;
            Excel.Workbook workbook = excelApp.Workbooks.Add();
            Excel.Worksheet worksheet = (Excel.Worksheet)workbook.Sheets[1];

            worksheet.Cells[1, 1].Value = documentTitle;
            worksheet.Range[worksheet.Cells[1, 1], worksheet.Cells[1, tableData.Count + 2]].Merge();
            worksheet.Cells[1, 1].HorizontalAlignment = Excel.XlHAlign.xlHAlignCenter;

            for (int i = 0; i < headersConfig.Count; i++)
            {
                worksheet.Cells[2 + i, 2].Value = headersConfig[i].title;
                worksheet.Cells[2 + i, 2].Font.Bold = true;
                worksheet.Rows[i + 1].RowHeight = headersConfig[i].height;
            }

            for (int colIndex = 0; colIndex < tableData.Count; colIndex++)
            {
                var data = tableData[colIndex];

                for (int rowIndex = 0; rowIndex < headersConfig.Count; rowIndex++)
                {
                    var property = data.GetType().GetProperty(headersConfig[rowIndex].propertyName);
                    if (property != null)
                    {
                        worksheet.Cells[rowIndex + 2, colIndex + 3].Value = property.GetValue(data);
                    }
                }
            }

            List<int> rows = new List<int>();
            foreach (var mergeInfo in mergeCellsInfo)
            {
                ValidateMergeCells(mergeCellsInfo);
                worksheet.Range[worksheet.Cells[mergeInfo.StartRow + 1, mergeInfo.StartCol],
                                worksheet.Cells[mergeInfo.EndRow + 1, mergeInfo.EndCol]].Merge();
                worksheet.Cells[mergeInfo.StartRow + 1, mergeInfo.StartCol].Value = mergeInfo.title;
                for (int r = mergeInfo.StartRow + 1; r <= mergeInfo.EndRow + 1; r++)
                {
                    rows.Add(r);
                }
            }

            for (int i = 2; i < headersConfig.Count + 2; i++)
            {
                if (!rows.Contains(i))
                {
                    worksheet.Range[worksheet.Cells[i, 1], worksheet.Cells[i, 2]].Merge();
                }
            }

            workbook.SaveAs(filePath);
            workbook.Close();
            Marshal.ReleaseComObject(workbook);
            excelApp.Quit();
            Marshal.ReleaseComObject(excelApp);
        }

        private void ValidateMergeCells(List<(int StartRow, int EndRow, int StartCol, int EndCol, string title)> mergeCellsInfo)
        {
            for (int i = 0; i < mergeCellsInfo.Count; i++)
            {
                for (int j = i + 1; j < mergeCellsInfo.Count; j++)
                {
                    if (CellsOverlap(mergeCellsInfo[i], mergeCellsInfo[j]))
                    {
                        throw new InvalidOperationException("Объединенные ячейки пересекаются.");
                    }
                }
            }
        }

        private bool CellsOverlap(
            (int StartRow, int EndRow, int StartCol, int EndCol, string title) range1,
            (int StartRow, int EndRow, int StartCol, int EndCol, string title) range2)
        {
            bool rowsOverlap = range1.StartRow <= range2.EndRow && range1.EndRow >= range2.StartRow;
            bool colsOverlap = range1.StartCol <= range2.EndCol && range1.EndCol >= range2.StartCol;
            return rowsOverlap && colsOverlap;
        }
    }
}