using UniversityBusinessLogic.OfficePackage.Models;
using UniversityContracts.ViewModels;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

namespace UniversityBusinessLogic.OfficePackage
{
    public class ExcelBuilderProvider
    {
        private readonly string tempFileName = "temp.xlsx";
        private SpreadsheetDocument? spreadsheetDocument;
        private SharedStringTablePart? shareStringPart;
        private Worksheet? worksheet;

        private void CreateStyles(WorkbookPart workbookpart)
        {
            var sp = workbookpart.AddNewPart<WorkbookStylesPart>();
            sp.Stylesheet = new Stylesheet();

            var fonts = new Fonts() { Count = 2U, KnownFonts = true };

            var fontUsual = new Font();
            fontUsual.Append(new FontSize() { Val = 12D });
            fonts.Append(fontUsual);

            var fontTitle = new Font();
            fontTitle.Append(new Bold());
            fontTitle.Append(new FontSize() { Val = 12D });
            fonts.Append(fontTitle);

            var fills = new Fills() { Count = 3U };

            var fill1 = new Fill();
            fill1.Append(new PatternFill()
            {
                PatternType = PatternValues.None
            });

            var fill2 = new Fill();
            fill2.Append(new PatternFill()
            {
                PatternType = PatternValues.Gray125
            });

            var fill3 = new Fill();
            fill3.Append(new PatternFill()
            {
                PatternType = PatternValues.Solid,
                ForegroundColor = new()
                {
                    Rgb = "e0e8ff"
                }
            });

            var fill4 = new Fill();
            fill1.Append(new PatternFill()
            {
                PatternType = PatternValues.None
            });

            fills.Append(fill1);
            fills.Append(fill2);
            fills.Append(fill3);
            fills.Append(fill4);

            var borders = new Borders() { Count = 2U };

            var borderNoBorder = new Border();
            borderNoBorder.Append(new LeftBorder());
            borderNoBorder.Append(new RightBorder());
            borderNoBorder.Append(new TopBorder());
            borderNoBorder.Append(new BottomBorder());
            borderNoBorder.Append(new DiagonalBorder());

            var borderThin = new Border();

            var leftBorder = new LeftBorder() { Style = BorderStyleValues.Thin };
            leftBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
            var rightBorder = new RightBorder() { Style = BorderStyleValues.Thin };
            rightBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
            var topBorder = new TopBorder() { Style = BorderStyleValues.Thin };
            topBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });
            var bottomBorder = new BottomBorder() { Style = BorderStyleValues.Thin };
            bottomBorder.Append(new DocumentFormat.OpenXml.Office2010.Excel.Color() { Indexed = 64U });

            borderThin.Append(leftBorder);
            borderThin.Append(rightBorder);
            borderThin.Append(topBorder);
            borderThin.Append(bottomBorder);
            borderThin.Append(new DiagonalBorder());

            borders.Append(borderNoBorder);
            borders.Append(borderThin);

            CellFormats cellFormats = new() { Count = 4U };
            CellFormat cellFormatEmpty = new()
            {
                FontId = 0U,
                FillId = 0U,
                BorderId = 1U,
                ApplyFont = true
            };
            CellFormat cellFormatDefault = new()
            {
                FontId = 0U,
                FillId = 3U,
                BorderId = 1U,
                ApplyFont = true
            };
            CellFormat cellFormatTitle = new()
            {
                FontId = 1U,
                FillId = 2U,
                BorderId = 1U,
                ApplyFont = true,
                ApplyBorder = true,
                Alignment = new Alignment()
                {
                    Vertical = VerticalAlignmentValues.Center,
                    Horizontal = HorizontalAlignmentValues.Center,
                    WrapText = true
                }
            };

            cellFormats.Append(cellFormatEmpty);
            cellFormats.Append(cellFormatDefault);
            cellFormats.Append(cellFormatTitle);

            sp.Stylesheet.Append(fonts);
            sp.Stylesheet.Append(fills);
            sp.Stylesheet.Append(borders);
            sp.Stylesheet.Append(cellFormats);
        }

        public void CreateDocument()
        {
            spreadsheetDocument = SpreadsheetDocument.Create(tempFileName,
                SpreadsheetDocumentType.Workbook);
            WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
            workbookpart.Workbook = new Workbook();

            CreateStyles(workbookpart);

            shareStringPart = spreadsheetDocument.WorkbookPart!
                .GetPartsOfType<SharedStringTablePart>()
                .Any()
                ? spreadsheetDocument.WorkbookPart
                    .GetPartsOfType<SharedStringTablePart>()
                    .First()
                : spreadsheetDocument.WorkbookPart
                    .AddNewPart<SharedStringTablePart>();

            if (shareStringPart.SharedStringTable == null)
            {
                shareStringPart.SharedStringTable = new SharedStringTable();
            }

            WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
            worksheetPart.Worksheet = new Worksheet(new SheetData());

            Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());
            Sheet sheet = new()
            {
                Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
                SheetId = 1,
                Name = "Лист"
            };
            sheets.Append(sheet);

            worksheet = worksheetPart.Worksheet;
        }

        public void InsertCellInWorksheet(ExcelCellData cellData)
        {
            if (worksheet == null || shareStringPart == null)
            {
                return;
            }
            var sheetData = worksheet.GetFirstChild<SheetData>();
            if (sheetData == null)
            {
                return;
            }

            Row? row = sheetData.Elements<Row>()
                .Where(r => r.RowIndex! == cellData.RowIndex)
                .FirstOrDefault();
            if (row == null)
            {
                row = new Row() { RowIndex = cellData.RowIndex };
                sheetData.Append(row);
            }

            Cell? cell = row.Elements<Cell>()
                .Where(c => c.CellReference!.Value == cellData.CellReference)
                .FirstOrDefault();
            if (cell == null)
            {
                Cell? refCell = null;
                foreach (Cell rowCell in row.Elements<Cell>())
                {
                    if (string.Compare(rowCell.CellReference!.Value, cellData.CellReference, true) > 0)
                    {
                        refCell = rowCell;
                        break;
                    }
                }
                var newCell = new Cell()
                {
                    CellReference = cellData.CellReference
                };
                row.InsertBefore(newCell, refCell);
                cell = newCell;
            }

            shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new Text(cellData.Text)));
            shareStringPart.SharedStringTable.Save();
            cell.CellValue = new CellValue((shareStringPart.SharedStringTable.Elements<SharedStringItem>().Count() - 1).ToString());
            cell.DataType = new EnumValue<CellValues>(CellValues.SharedString);
            cell.StyleIndex = cellData.StyleIndex;
        }

        private void MergeCells(ExcelMergeParameters excelParams)
        {
            if (worksheet == null)
            {
                return;
            }
            MergeCells mergeCells;
            if (worksheet.Elements<MergeCells>().Any())
            {
                mergeCells = worksheet.Elements<MergeCells>().First();
            }
            else
            {
                mergeCells = new MergeCells();
                if (worksheet.Elements<CustomSheetView>().Any())
                {
                    worksheet.InsertAfter(mergeCells, worksheet.Elements<CustomSheetView>().First());
                }
                else
                {
                    worksheet.InsertAfter(mergeCells, worksheet.Elements<SheetData>().First());
                }
            }
            var mergeCell = new MergeCell()
            {
                Reference = new StringValue(excelParams.Merge)
            };
            mergeCells.Append(mergeCell);
        }

        private void Save()
        {
            if (spreadsheetDocument == null)
            {
                return;
            }
            spreadsheetDocument.WorkbookPart!.Workbook.Save();
            spreadsheetDocument.Dispose();
        }

        public byte[] GetFile()
        {
            Save();
            byte[] file = File.ReadAllBytes(tempFileName);
            File.Delete(tempFileName);
            return file;
        }

        public void CreateTitle(string text)
        {
            MergeCells(new ExcelMergeParameters
            {
                CellFromName = "A1",
                CellToName = "B1"
            });
            InsertCellInWorksheet(new ExcelCellData
            {
                ColumnName = "A",
                RowIndex = 1,
                Text = text,
                StyleIndex = 0
            });
        }

        public void CreateStudentsDisciplineTable(List<ReportStudentsDisciplineViewModel> data)
        {
            if (worksheet == null || shareStringPart == null)
            {
                return;
            }

            Columns columns = new();
            Column columnA = new() { Min = 1, Max = 1, Width = 30, CustomWidth = true };
            columns.Append(columnA);
            Column columnB = new() { Min = 2, Max = 2, Width = 30, CustomWidth = true };
            columns.Append(columnB);
            worksheet.InsertAt(columns, 0);

            InsertCellInWorksheet(new ExcelCellData
            {
                ColumnName = "A",
                RowIndex = 2,
                Text = "Студент",
                StyleIndex = 2
            });
            InsertCellInWorksheet(new ExcelCellData
            {
                ColumnName = "B",
                RowIndex = 2,
                Text = "Дисциплина",
                StyleIndex = 2
            });

            uint currentRow = 3;
            foreach (ReportStudentsDisciplineViewModel student in data)
            {
                InsertCellInWorksheet(new ExcelCellData
                {
                    ColumnName = "A",
                    RowIndex = currentRow,
                    Text = student.Student,
                    StyleIndex = 1
                });
                InsertCellInWorksheet(new ExcelCellData
                {
                    ColumnName = "B",
                    RowIndex = currentRow,
                    Text = "",
                    StyleIndex = 1
                });
                currentRow++;
                foreach (string discipline in student.Disciplines)
                {
                    InsertCellInWorksheet(new ExcelCellData
                    {
                        ColumnName = "A",
                        RowIndex = currentRow,
                        Text = "",
                        StyleIndex = 1
                    });
                    InsertCellInWorksheet(new ExcelCellData
                    {
                        ColumnName = "B",
                        RowIndex = currentRow,
                        Text = discipline,
                        StyleIndex = 1
                    });
                    currentRow++;
                }
            }
        }
    }
}