using ClosedXML.Excel;

namespace Eis.Infrastructure.Administration;

internal static class AdminArrangementExporter
{
    private static readonly string[] InformationHeaders =
    [
        "考生学校", "班级", "报名号", "姓名", "证件号码", "考试代码", "考试名称", "准考证号",
        "考点代码", "考点名称", "考点详细地址", "科目", "日期", "时间", "考试考场序号",
        "考场通用名称", "物理场地代码", "楼栋", "楼层", "座位号"
    ];

    public static byte[] AdmitCardInformation(
        IReadOnlyList<ArrangementExportRow> rows,
        string subtitle)
    {
        using var workbook = new XLWorkbook();
        var sheet = workbook.Worksheets.Add("准考证信息");
        ConfigureTable(
            sheet,
            $"准考证信息台账｜{subtitle}",
            InformationHeaders,
            rows.Select(InformationValues).ToArray());
        return Save(workbook);
    }

    public static byte[] CenterMaterials(
        IReadOnlyList<ArrangementExportRow> rows,
        string subtitle)
    {
        using var workbook = new XLWorkbook();
        var sorted = rows
            .OrderBy(item => item.CenterCode, StringComparer.Ordinal)
            .ThenBy(item => item.SubjectDate, StringComparer.Ordinal)
            .ThenBy(item => item.SubjectTime, StringComparer.Ordinal)
            .ThenBy(item => item.ExamRoomCode, StringComparer.Ordinal)
            .ThenBy(item => item.Seat, StringComparer.Ordinal)
            .ToArray();
        AddDeskStickers(workbook.Worksheets.Add("桌贴"), sorted);
        var doors = sorted
            .GroupBy(
                item => new
                {
                    item.ExamCode,
                    item.SubjectName,
                    item.CenterCode,
                    item.ExamRoomCode,
                    item.RoomCode
                })
            .Select(group => (Row: group.First(), Count: group.Count()))
            .ToArray();
        ConfigureTable(
            workbook.Worksheets.Add("门贴"),
            $"考场门贴｜{subtitle}",
            [
                "考试", "科目", "日期", "时间", "考点", "考试考场序号",
                "考场通用名称", "场地代码", "楼栋", "楼层", "人数"
            ],
            doors.Select(item => new object[]
            {
                item.Row.ExamName,
                item.Row.SubjectName,
                item.Row.SubjectDate,
                item.Row.SubjectTime,
                item.Row.CenterName,
                item.Row.ExamRoomCode,
                item.Row.RoomName,
                item.Row.RoomCode,
                item.Row.Building,
                item.Row.Floor,
                item.Count
            }).ToArray());
        ConfigureTable(
            workbook.Worksheets.Add("考场签名单"),
            $"考场考生签名单｜{subtitle}",
            [
                "考试", "科目", "日期", "时间", "考试考场序号", "考场通用名称",
                "楼栋", "楼层", "座位号", "准考证号", "姓名", "考生学校", "班级", "考生签名"
            ],
            sorted.Select(item => new object[]
            {
                item.ExamName,
                item.SubjectName,
                item.SubjectDate,
                item.SubjectTime,
                item.ExamRoomCode,
                item.RoomName,
                item.Building,
                item.Floor,
                item.Seat,
                item.CardNumber,
                item.CandidateName,
                item.SchoolName,
                item.ClassName,
                ""
            }).ToArray());
        return Save(workbook);
    }

    private static void AddDeskStickers(
        IXLWorksheet sheet,
        IReadOnlyList<ArrangementExportRow> rows)
    {
        sheet.ShowGridLines = false;
        for (var column = 1; column <= 7; column++)
            sheet.Column(column).Width = column == 4 ? 3 : 14.5;
        for (var index = 0; index < rows.Count; index++)
        {
            var item = rows[index];
            var slot = index % 2;
            var group = index / 2;
            var startRow = group * 9 + 1;
            var startColumn = slot == 0 ? 1 : 5;
            var values = new[]
            {
                item.ExamName,
                $"{item.SubjectName}｜{item.SubjectDate} {item.SubjectTime}",
                "考试考场序号",
                item.ExamRoomCode,
                $"座位号  {item.Seat}",
                $"{item.RoomName}（场地 {item.RoomCode}）｜{item.Building} {item.Floor}",
                $"{item.CandidateName}｜{item.SchoolName} {item.ClassName}",
                $"{item.CenterName}｜准考证号 {item.CardNumber}"
            };
            for (var offset = 0; offset < values.Length; offset++)
            {
                var range = sheet.Range(
                    startRow + offset,
                    startColumn,
                    startRow + offset,
                    startColumn + 2);
                range.Merge();
                range.Value = values[offset];
                range.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
                range.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
                range.Style.Alignment.WrapText = true;
                range.Style.Border.OutsideBorder = XLBorderStyleValues.Medium;
                range.Style.Border.InsideBorder = XLBorderStyleValues.Hair;
                sheet.Row(startRow + offset).Height = 28;
            }
            var title = sheet.Range(startRow, startColumn, startRow, startColumn + 2);
            title.Style.Fill.BackgroundColor = XLColor.FromHtml("#173F60");
            title.Style.Font.FontColor = XLColor.White;
            title.Style.Font.Bold = true;
            var room = sheet.Range(startRow + 3, startColumn, startRow + 3, startColumn + 2);
            room.Style.Font.FontSize = 24;
            room.Style.Font.Bold = true;
            var seat = sheet.Range(startRow + 4, startColumn, startRow + 4, startColumn + 2);
            seat.Style.Fill.BackgroundColor = XLColor.FromHtml("#FFF1C7");
            seat.Style.Font.FontColor = XLColor.FromHtml("#8A4B08");
            seat.Style.Font.FontSize = 18;
            seat.Style.Font.Bold = true;
        }
        sheet.PageSetup.PageOrientation = XLPageOrientation.Portrait;
        sheet.PageSetup.FitToPages(1, 0);
        sheet.PageSetup.Margins.SetLeft(0.25);
        sheet.PageSetup.Margins.SetRight(0.25);
        sheet.PageSetup.Margins.SetTop(0.25);
        sheet.PageSetup.Margins.SetBottom(0.25);
    }

    private static void ConfigureTable(
        IXLWorksheet sheet,
        string title,
        IReadOnlyList<string> headers,
        IReadOnlyList<object[]> rows)
    {
        sheet.ShowGridLines = false;
        var lastColumn = headers.Count;
        var titleRange = sheet.Range(1, 1, 1, lastColumn);
        titleRange.Merge();
        titleRange.Value = title;
        titleRange.Style.Fill.BackgroundColor = XLColor.FromHtml("#173F60");
        titleRange.Style.Font.FontColor = XLColor.White;
        titleRange.Style.Font.FontSize = 16;
        titleRange.Style.Font.Bold = true;
        titleRange.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
        sheet.Row(1).Height = 28;
        for (var column = 0; column < headers.Count; column++)
        {
            var cell = sheet.Cell(2, column + 1);
            cell.Value = headers[column];
            cell.Style.Fill.BackgroundColor = XLColor.FromHtml("#2C7080");
            cell.Style.Font.FontColor = XLColor.White;
            cell.Style.Font.Bold = true;
            cell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
            cell.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
            cell.Style.Alignment.WrapText = true;
        }
        for (var rowIndex = 0; rowIndex < rows.Count; rowIndex++)
        {
            for (var column = 0; column < headers.Count; column++)
            {
                var value = column < rows[rowIndex].Length ? rows[rowIndex][column] : "";
                var cell = sheet.Cell(rowIndex + 3, column + 1);
                if (value is int integer) cell.Value = integer;
                else if (value is double number) cell.Value = number;
                else cell.Value = Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture) ?? "";
                cell.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
                cell.Style.Alignment.WrapText = true;
                cell.Style.Border.BottomBorder = XLBorderStyleValues.Hair;
                cell.Style.Border.BottomBorderColor = XLColor.FromHtml("#D8E2E7");
            }
        }
        sheet.SheetView.FreezeRows(2);
        if (rows.Count > 0) sheet.Range(2, 1, rows.Count + 2, lastColumn).SetAutoFilter();
        sheet.Columns().AdjustToContents(8, 40);
        sheet.PageSetup.PageOrientation = XLPageOrientation.Landscape;
        sheet.PageSetup.FitToPages(1, 0);
        sheet.PageSetup.SetRowsToRepeatAtTop(1, 2);
    }

    private static object[] InformationValues(ArrangementExportRow item) =>
    [
        item.SchoolName,
        item.ClassName,
        item.CandidateNumber,
        item.CandidateName,
        item.IdNumber,
        item.ExamCode,
        item.ExamName,
        item.CardNumber,
        item.CenterCode,
        item.CenterName,
        item.CenterAddress,
        item.SubjectName,
        item.SubjectDate,
        item.SubjectTime,
        item.ExamRoomCode,
        item.RoomName,
        item.RoomCode,
        item.Building,
        item.Floor,
        item.Seat
    ];

    private static byte[] Save(XLWorkbook workbook)
    {
        using var stream = new MemoryStream();
        workbook.SaveAs(stream);
        return stream.ToArray();
    }
}
