using ClosedXML.Excel;
using SportsDivision.Application.DTOs;
using SportsDivision.Application.DTOs.ReportDtos;

namespace SportsDivision.Web.Reports;

/// <summary>
/// Builds Microsoft Excel (.xlsx) workbooks for each report. One worksheet per
/// report, with a title, subtitle (tournament name), a styled header row, and
/// flattened data rows for the nested reports.
/// </summary>
public static class ReportExcelExporter
{
    public const string MimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

    private static int WriteHeader(IXLWorksheet ws, string title, string subtitle, string[] columns)
    {
        ws.Cell(1, 1).Value = title;
        ws.Cell(1, 1).Style.Font.Bold = true;
        ws.Cell(1, 1).Style.Font.FontSize = 14;
        ws.Range(1, 1, 1, Math.Max(1, columns.Length)).Merge();

        ws.Cell(2, 1).Value = subtitle;
        ws.Cell(2, 1).Style.Font.Italic = true;
        ws.Range(2, 1, 2, Math.Max(1, columns.Length)).Merge();

        const int headerRow = 4;
        for (int i = 0; i < columns.Length; i++)
        {
            var c = ws.Cell(headerRow, i + 1);
            c.Value = columns[i];
            c.Style.Font.Bold = true;
            c.Style.Fill.BackgroundColor = XLColor.FromHtml("#003366");
            c.Style.Font.FontColor = XLColor.White;
        }
        return headerRow + 1;
    }

    private static byte[] Finish(XLWorkbook wb, params IXLWorksheet[] sheets)
    {
        foreach (var ws in sheets)
        {
            ws.SheetView.FreezeRows(4);
            ws.Columns().AdjustToContents();
        }
        using var ms = new MemoryStream();
        wb.SaveAs(ms);
        return ms.ToArray();
    }

    private static void SetInt(IXLCell cell, int? v) { if (v.HasValue) cell.Value = v.Value; else cell.Value = "-"; }
    private static void SetDec(IXLCell cell, decimal? v) { if (v.HasValue) cell.Value = v.Value; else cell.Value = "-"; }

    public static byte[] PopularEvents(IEnumerable<PopularEventsReportDto> data, string tournamentName)
    {
        using var wb = new XLWorkbook();
        var ws = wb.AddWorksheet("Popular Events");
        int r = WriteHeader(ws, "Popular Events Report", tournamentName,
            new[] { "Event", "Category", "Total", "Male", "Female" });
        foreach (var e in data)
        {
            ws.Cell(r, 1).Value = e.EventName;
            ws.Cell(r, 2).Value = e.Category;
            ws.Cell(r, 3).Value = e.RegistrationCount;
            ws.Cell(r, 4).Value = e.MaleCount;
            ws.Cell(r, 5).Value = e.FemaleCount;
            r++;
        }
        return Finish(wb, ws);
    }

    public static byte[] RegistrationByGender(IEnumerable<RegistrationByGenderReportDto> data, string tournamentName)
    {
        using var wb = new XLWorkbook();
        var ws = wb.AddWorksheet("Registration by Gender");
        int r = WriteHeader(ws, "Registration by Gender", tournamentName,
            new[] { "Event", "Level", "Male", "Female", "Total" });
        foreach (var e in data)
        {
            ws.Cell(r, 1).Value = e.EventName;
            ws.Cell(r, 2).Value = e.EventLevelName;
            ws.Cell(r, 3).Value = e.MaleCount;
            ws.Cell(r, 4).Value = e.FemaleCount;
            ws.Cell(r, 5).Value = e.TotalCount;
            r++;
        }
        return Finish(wb, ws);
    }

    public static byte[] EventSchoolReport(IEnumerable<EventSchoolReportDto> data, string tournamentName)
    {
        using var wb = new XLWorkbook();
        var ws = wb.AddWorksheet("Event School Report");
        int r = WriteHeader(ws, "Event School Report", tournamentName,
            new[] { "Event", "Level", "School", "Student", "Performance", "Placement", "Points" });
        foreach (var ev in data)
            foreach (var school in ev.Schools)
                foreach (var s in school.Students)
                {
                    ws.Cell(r, 1).Value = ev.EventName;
                    ws.Cell(r, 2).Value = ev.EventLevelName;
                    ws.Cell(r, 3).Value = school.SchoolName;
                    ws.Cell(r, 4).Value = s.StudentName;
                    SetDec(ws.Cell(r, 5), s.RawPerformance);
                    SetInt(ws.Cell(r, 6), s.Placement);
                    ws.Cell(r, 7).Value = s.PlacementPoints;
                    r++;
                }
        return Finish(wb, ws);
    }

    public static byte[] StudentsBySchool(IEnumerable<StudentsBySchoolReportDto> data, string tournamentName)
    {
        using var wb = new XLWorkbook();
        var ws = wb.AddWorksheet("Students by School");
        int r = WriteHeader(ws, "Students by School", tournamentName,
            new[] { "School", "Zone", "Student", "Sex", "Events" });
        foreach (var school in data)
            foreach (var s in school.Students)
            {
                ws.Cell(r, 1).Value = school.SchoolName;
                ws.Cell(r, 2).Value = school.ZoneName;
                ws.Cell(r, 3).Value = s.StudentName;
                ws.Cell(r, 4).Value = s.Sex;
                ws.Cell(r, 5).Value = string.Join(", ", s.Events);
                r++;
            }
        return Finish(wb, ws);
    }

    public static byte[] ScoresByEvent(IEnumerable<ScoresByEventReportDto> data, string tournamentName)
    {
        using var wb = new XLWorkbook();
        var ws = wb.AddWorksheet("Scores by Event");
        int r = WriteHeader(ws, "Scores by Event", tournamentName,
            new[] { "Event", "Level", "Category", "Placement", "Student", "School", "Performance", "WA Points", "Placement Points" });
        foreach (var ev in data)
            foreach (var s in ev.Scores)
            {
                ws.Cell(r, 1).Value = ev.EventName;
                ws.Cell(r, 2).Value = ev.EventLevelName;
                ws.Cell(r, 3).Value = ev.Category;
                SetInt(ws.Cell(r, 4), s.Placement);
                ws.Cell(r, 5).Value = s.StudentName;
                ws.Cell(r, 6).Value = s.SchoolName;
                ws.Cell(r, 7).Value = s.RawPerformance;
                ws.Cell(r, 8).Value = s.CalculatedPoints;
                ws.Cell(r, 9).Value = s.PlacementPoints;
                r++;
            }
        return Finish(wb, ws);
    }

    public static byte[] StudentPoints(IEnumerable<StudentPointsReportDto> data, string tournamentName)
    {
        using var wb = new XLWorkbook();
        var ws = wb.AddWorksheet("Student Points");
        int r = WriteHeader(ws, "Student Points", tournamentName,
            new[] { "Student", "School", "Sex", "Total Placement Points", "Events" });
        foreach (var s in data)
        {
            ws.Cell(r, 1).Value = s.StudentName;
            ws.Cell(r, 2).Value = s.SchoolName;
            ws.Cell(r, 3).Value = s.Sex;
            ws.Cell(r, 4).Value = s.TotalPlacementPoints;
            ws.Cell(r, 5).Value = s.EventCount;
            r++;
        }
        return Finish(wb, ws);
    }

    public static byte[] SchoolStandings(IEnumerable<SchoolPointsSummaryDto> data, string tournamentName)
    {
        using var wb = new XLWorkbook();
        var ws = wb.AddWorksheet("School Standings");
        int r = WriteHeader(ws, "School Standings", tournamentName,
            new[] { "Rank", "School", "Short Name", "Total Points", "1st Place", "2nd Place", "3rd Place" });
        int rank = 1;
        foreach (var s in data)
        {
            ws.Cell(r, 1).Value = rank++;
            ws.Cell(r, 2).Value = s.SchoolName;
            ws.Cell(r, 3).Value = s.ShortName ?? string.Empty;
            ws.Cell(r, 4).Value = s.TotalPoints;
            ws.Cell(r, 5).Value = s.FirstPlaceCount;
            ws.Cell(r, 6).Value = s.SecondPlaceCount;
            ws.Cell(r, 7).Value = s.ThirdPlaceCount;
            r++;
        }
        return Finish(wb, ws);
    }
}
