using ClosedXML.Excel;

namespace Jiaowu.Api.Infrastructure.Exams;

public static class ExamSignInWorkbookExporter
{
    private static readonly XLColor Navy = XLColor.FromHtml("#173E72");
    private static readonly XLColor Teal = XLColor.FromHtml("#24706A");
    private static readonly XLColor Pale = XLColor.FromHtml("#EFF3F6");
    private static readonly XLColor Rule = XLColor.FromHtml("#C8D3DA");

    public static byte[] Create(ExamSignInWorkbookData data)
    {
        using var workbook = new XLWorkbook();
        AddSummarySheet(workbook, data);

        var usedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
        {
            "考场汇总"
        };
        foreach (var session in data.Sessions
                     .OrderBy(x => x.ExamDate)
                     .ThenBy(x => x.StartsAt)
                     .ThenBy(x => x.BuildingName)
                     .ThenBy(x => x.ClassroomName)
                     .ThenBy(x => x.CourseCode))
        {
            AddSignInSheet(
                workbook,
                data,
                session,
                UniqueSheetName(session, usedNames));
        }

        using var stream = new MemoryStream();
        workbook.SaveAs(stream);
        return stream.ToArray();
    }

    private static void AddSummarySheet(
        XLWorkbook workbook,
        ExamSignInWorkbookData data)
    {
        var sheet = workbook.Worksheets.Add("考场汇总");
        sheet.Style.Font.FontName = "Microsoft YaHei";
        sheet.ShowGridLines = false;

        sheet.Range("A1:G1").Merge();
        sheet.Cell("A1").Value = $"{data.PlanName} · 考场签名单";
        StyleTitle(sheet.Range("A1:G1"));
        sheet.Row(1).Height = 34;

        sheet.Range("A2:G2").Merge();
        sheet.Cell("A2").Value =
            $"{data.TermName} · 共 {data.Sessions.Count} 个场次 · 导出时间：{DateTime.Now:yyyy-MM-dd HH:mm}";
        StyleSubTitle(sheet.Range("A2:G2"));
        sheet.Row(2).Height = 24;

        var headers = new[] { "日期", "时间", "考场", "课程", "教学班", "考生人数", "监考教师" };
        for (var column = 1; column <= headers.Length; column++)
            sheet.Cell(4, column).Value = headers[column - 1];
        StyleHeader(sheet.Range(4, 1, 4, headers.Length));

        var row = 5;
        foreach (var session in data.Sessions
                     .OrderBy(x => x.ExamDate)
                     .ThenBy(x => x.StartsAt)
                     .ThenBy(x => x.BuildingName)
                     .ThenBy(x => x.ClassroomName))
        {
            sheet.Cell(row, 1).Value = session.ExamDate.ToDateTime(TimeOnly.MinValue);
            sheet.Cell(row, 1).Style.DateFormat.Format = "yyyy-mm-dd";
            sheet.Cell(row, 2).Value = $"{session.StartsAt:HH:mm}-{session.EndsAt:HH:mm}";
            sheet.Cell(row, 3).Value = Location(session);
            sheet.Cell(row, 4).Value = $"{session.CourseCode} {session.CourseName}";
            sheet.Cell(row, 5).Value = session.TaskNumber;
            sheet.Cell(row, 6).Value = session.Students.Count;
            sheet.Cell(row, 7).Value = Invigilators(session);
            row++;
        }

        if (row > 5)
        {
            var table = sheet.Range(4, 1, row - 1, headers.Length);
            table.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
            table.Style.Border.InsideBorderColor = Rule;
            table.Style.Border.OutsideBorder = XLBorderStyleValues.Medium;
            table.Style.Border.OutsideBorderColor = Rule;
            table.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
            sheet.Rows(5, row - 1).Height = 25;
        }

        sheet.Column(1).Width = 13;
        sheet.Column(2).Width = 15;
        sheet.Column(3).Width = 24;
        sheet.Column(4).Width = 30;
        sheet.Column(5).Width = 22;
        sheet.Column(6).Width = 11;
        sheet.Column(7).Width = 24;
        sheet.Columns(3, 7).Style.Alignment.WrapText = true;
        sheet.SheetView.FreezeRows(4);
        sheet.PageSetup.PageOrientation = XLPageOrientation.Landscape;
        sheet.PageSetup.PaperSize = XLPaperSize.A4Paper;
        sheet.PageSetup.FitToPages(1, 0);
        sheet.PageSetup.Margins
            .SetLeft(0.25).SetRight(0.25).SetTop(0.35).SetBottom(0.35);
    }

    private static void AddSignInSheet(
        XLWorkbook workbook,
        ExamSignInWorkbookData data,
        ExamSignInSessionData session,
        string sheetName)
    {
        var sheet = workbook.Worksheets.Add(sheetName);
        sheet.Style.Font.FontName = "Microsoft YaHei";
        sheet.ShowGridLines = false;

        sheet.Range("A1:G1").Merge();
        sheet.Cell("A1").Value = "考试考场签名单";
        StyleTitle(sheet.Range("A1:G1"));
        sheet.Cell("A1").Style.Alignment.Horizontal =
            XLAlignmentHorizontalValues.Center;
        sheet.Row(1).Height = 36;

        sheet.Range("A2:G2").Merge();
        sheet.Cell("A2").Value = $"{data.PlanName} · {data.TermName}";
        StyleSubTitle(sheet.Range("A2:G2"));
        sheet.Cell("A2").Style.Alignment.Horizontal =
            XLAlignmentHorizontalValues.Center;
        sheet.Row(2).Height = 23;

        sheet.Range("A3:D3").Merge();
        sheet.Cell("A3").Value =
            $"课程：{session.CourseCode} {session.CourseName}（{session.TaskNumber}）";
        sheet.Range("E3:G3").Merge();
        sheet.Cell("E3").Value = $"考生人数：{session.Students.Count} 人";

        sheet.Range("A4:D4").Merge();
        sheet.Cell("A4").Value =
            $"考试时间：{session.ExamDate:yyyy-MM-dd} {session.StartsAt:HH:mm}-{session.EndsAt:HH:mm}";
        sheet.Range("E4:G4").Merge();
        sheet.Cell("E4").Value = $"考场：{Location(session)}";

        sheet.Range("A5:D5").Merge();
        sheet.Cell("A5").Value = $"监考教师：{Invigilators(session)}";
        sheet.Range("E5:G5").Merge();
        sheet.Cell("E5").Value = "应到：______  实到：______  缺考：______";

        var metadata = sheet.Range("A3:G5");
        metadata.Style.Fill.BackgroundColor = XLColor.White;
        metadata.Style.Font.FontColor = XLColor.FromHtml("#304B60");
        metadata.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
        metadata.Style.Alignment.WrapText = true;
        metadata.Style.Border.BottomBorder = XLBorderStyleValues.Thin;
        metadata.Style.Border.BottomBorderColor = Rule;
        sheet.Rows(3, 5).Height = 24;

        var headers = new[] { "序号", "座位号", "学号", "姓名", "行政班", "考生签名", "备注" };
        for (var column = 1; column <= headers.Length; column++)
            sheet.Cell(7, column).Value = headers[column - 1];
        StyleHeader(sheet.Range(7, 1, 7, headers.Length));
        sheet.Row(7).Height = 27;

        var row = 8;
        foreach (var student in session.Students
                     .OrderBy(x => x.SeatNumber)
                     .ThenBy(x => x.StudentNumber))
        {
            var index = row - 7;
            sheet.Cell(row, 1).Value = index;
            sheet.Cell(row, 2).Value =
                (student.SeatNumber > 0 ? student.SeatNumber : index)
                .ToString("D3");
            sheet.Cell(row, 3).Value = student.StudentNumber;
            sheet.Cell(row, 4).Value = student.Name;
            sheet.Cell(row, 5).Value = student.ClassName;
            sheet.Row(row).Height = 28;
            row++;
        }

        if (session.Students.Count == 0)
        {
            sheet.Range("A8:G8").Merge();
            sheet.Cell("A8").Value = "本场次暂无考生";
            sheet.Cell("A8").Style
                .Font.SetFontColor(XLColor.FromHtml("#8A5A20"))
                .Fill.SetBackgroundColor(XLColor.FromHtml("#FFF8EA"))
                .Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center);
            sheet.Row(8).Height = 28;
            row = 9;
        }

        var roster = sheet.Range(7, 1, row - 1, headers.Length);
        roster.Style.Border.InsideBorder = XLBorderStyleValues.Thin;
        roster.Style.Border.InsideBorderColor = Rule;
        roster.Style.Border.OutsideBorder = XLBorderStyleValues.Medium;
        roster.Style.Border.OutsideBorderColor = XLColor.FromHtml("#9CADB7");
        roster.Style.Alignment.Vertical = XLAlignmentVerticalValues.Center;
        sheet.Range(8, 1, row - 1, 4).Style.Alignment.Horizontal =
            XLAlignmentHorizontalValues.Center;
        sheet.Range(8, 5, row - 1, 7).Style.Alignment.WrapText = true;

        sheet.Column(1).Width = 7;
        sheet.Column(2).Width = 10;
        sheet.Column(3).Width = 16;
        sheet.Column(4).Width = 12;
        sheet.Column(5).Width = 20;
        sheet.Column(6).Width = 22;
        sheet.Column(7).Width = 17;
        sheet.SheetView.FreezeRows(7);
        sheet.PageSetup.PageOrientation = XLPageOrientation.Portrait;
        sheet.PageSetup.PaperSize = XLPaperSize.A4Paper;
        sheet.PageSetup.FitToPages(1, 0);
        sheet.PageSetup.Margins
            .SetLeft(0.25).SetRight(0.25).SetTop(0.3).SetBottom(0.3);
    }

    private static void StyleTitle(IXLRange range) =>
        range.Style
            .Font.SetBold()
            .Font.SetFontSize(18)
            .Font.SetFontColor(XLColor.White)
            .Fill.SetBackgroundColor(Navy)
            .Alignment.SetVertical(XLAlignmentVerticalValues.Center);

    private static void StyleSubTitle(IXLRange range) =>
        range.Style
            .Font.SetFontColor(XLColor.FromHtml("#52677A"))
            .Fill.SetBackgroundColor(Pale)
            .Alignment.SetVertical(XLAlignmentVerticalValues.Center);

    private static void StyleHeader(IXLRange range) =>
        range.Style
            .Font.SetBold()
            .Font.SetFontColor(XLColor.White)
            .Fill.SetBackgroundColor(Teal)
            .Alignment.SetHorizontal(XLAlignmentHorizontalValues.Center)
            .Alignment.SetVertical(XLAlignmentVerticalValues.Center);

    private static string Location(ExamSignInSessionData session) =>
        string.Join(" · ", new[]
        {
            session.BuildingName,
            session.ClassroomName
        }.Where(x => !string.IsNullOrWhiteSpace(x))) is { Length: > 0 } value
            ? value
            : "待分配考场";

    private static string Invigilators(ExamSignInSessionData session) =>
        session.InvigilatorNames.Count > 0
            ? string.Join('、', session.InvigilatorNames)
            : "待分配";

    private static string UniqueSheetName(
        ExamSignInSessionData session,
        ISet<string> usedNames)
    {
        var location = string.IsNullOrWhiteSpace(session.ClassroomName)
            ? "待分配"
            : session.ClassroomName;
        var raw = $"{session.ExamDate:MMdd}-{location}-{session.CourseCode}";
        var invalidCharacters = new HashSet<char>(":\\/?*[]");
        var sanitized = new string(raw
            .Select(character => invalidCharacters.Contains(character) ? '-' : character)
            .ToArray()).Trim(' ', '\'');
        if (sanitized.Length == 0) sanitized = "考场";
        if (sanitized.Length > 31) sanitized = sanitized[..31];

        var candidate = sanitized;
        var suffix = 2;
        while (!usedNames.Add(candidate))
        {
            var marker = $"-{suffix++}";
            var prefixLength = Math.Min(sanitized.Length, 31 - marker.Length);
            candidate = sanitized[..prefixLength] + marker;
        }
        return candidate;
    }
}

public sealed record ExamSignInWorkbookData(
    string PlanName,
    string TermName,
    IReadOnlyList<ExamSignInSessionData> Sessions);

public sealed record ExamSignInSessionData(
    Guid SessionId,
    DateOnly ExamDate,
    DateTime StartsAt,
    DateTime EndsAt,
    string CourseCode,
    string CourseName,
    string TaskNumber,
    string? BuildingName,
    string? ClassroomName,
    IReadOnlyList<string> InvigilatorNames,
    IReadOnlyList<ExamSignInStudentData> Students);

public sealed record ExamSignInStudentData(
    Guid StudentId,
    string StudentNumber,
    string Name,
    string ClassName,
    int SeatNumber = 0);
