using ClosedXML.Excel;

namespace Shared.Excel;

public sealed class ClosedXmlWorkbookService
{
    public IReadOnlyList<string> ReadHeaders(Stream stream)
    {
        using var workbook = new XLWorkbook(stream);
        var sheet = workbook.Worksheets.First();
        var last = sheet.Row(1).LastCellUsed()?.Address.ColumnNumber ?? 0;
        if (last == 0) return [];

        return Enumerable.Range(1, last)
            .Select(i => sheet.Cell(1, i).GetFormattedString().Trim())
            .Where(x => !string.IsNullOrWhiteSpace(x))
            .ToArray();
    }

    public IEnumerable<ImportRawRow> ReadRows(Stream stream, int maxRows)
    {
        using var workbook = new XLWorkbook(stream);
        var sheet = workbook.Worksheets.First();
        var lastColumn = sheet.Row(1).LastCellUsed()?.Address.ColumnNumber ?? 0;
        var lastRow = sheet.LastRowUsed()?.RowNumber() ?? 1;
        var headers = Enumerable.Range(1, lastColumn)
            .Select(i => sheet.Cell(1, i).GetFormattedString().Trim())
            .ToArray();

        if (lastRow - 1 > maxRows)
            throw new InvalidOperationException($"최대 {maxRows:N0}행까지 업로드할 수 있습니다.");

        for (var rowNo = 2; rowNo <= lastRow; rowNo++)
        {
            var values = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
            var hasValue = false;
            for (var column = 1; column <= lastColumn; column++)
            {
                var header = headers[column - 1];
                if (string.IsNullOrWhiteSpace(header)) continue;
                var value = sheet.Cell(rowNo, column).GetFormattedString().Trim();
                if (!string.IsNullOrWhiteSpace(value)) hasValue = true;
                values[header] = string.IsNullOrWhiteSpace(value) ? null : value;
            }
            if (hasValue) yield return new ImportRawRow(rowNo, values);
        }
    }

    public MemoryStream CreateTemplate(ImportDefinition definition)
    {
        var workbook = new XLWorkbook();
        var sheet = workbook.AddWorksheet("업로드");
        var fields = definition.Fields.Where(x => x.Importable).ToArray();

        for (var i = 0; i < fields.Length; i++)
        {
            var cell = sheet.Cell(1, i + 1);
            cell.Value = fields[i].Label;
            cell.Style.Font.Bold = true;
            cell.Style.Fill.BackgroundColor = XLColor.LightGray;
            cell.Comment.AddText(fields[i].Required ? "필수 입력" : "선택 입력");
        }

        sheet.SheetView.FreezeRows(1);
        sheet.Columns(1, fields.Length).Width = 18;
        sheet.Row(1).Height = 22;
        sheet.Range(1, 1, 1, fields.Length).SetAutoFilter();

        var help = workbook.AddWorksheet("필드설명");
        help.Cell("A1").Value = "필드";
        help.Cell("B1").Value = "필수";
        help.Cell("C1").Value = "형식";
        help.Cell("D1").Value = "허용 별칭";
        help.Range("A1:D1").Style.Font.Bold = true;
        for (var i = 0; i < fields.Length; i++)
        {
            var row = i + 2;
            help.Cell(row, 1).Value = fields[i].Label;
            help.Cell(row, 2).Value = fields[i].Required ? "Y" : "N";
            help.Cell(row, 3).Value = fields[i].DataType;
            help.Cell(row, 4).Value = string.Join(", ", fields[i].Aliases);
        }
        help.Columns().AdjustToContents(8, 35);

        var output = new MemoryStream();
        workbook.SaveAs(output);
        output.Position = 0;
        workbook.Dispose();
        return output;
    }

    public MemoryStream CreateErrorWorkbook(
        Stream source,
        IReadOnlyDictionary<int, IReadOnlyList<ImportIssue>> issues)
    {
        using var workbook = new XLWorkbook(source);
        var sheet = workbook.Worksheets.First();
        var errorColumn = (sheet.Row(1).LastCellUsed()?.Address.ColumnNumber ?? 0) + 1;
        sheet.Cell(1, errorColumn).Value = "오류사유";
        sheet.Cell(1, errorColumn).Style.Font.Bold = true;

        foreach (var (rowNo, rowIssues) in issues)
        {
            sheet.Cell(rowNo, errorColumn).Value = string.Join(" | ", rowIssues.Select(x => x.Message));
        }
        sheet.Column(errorColumn).Width = 45;

        var output = new MemoryStream();
        workbook.SaveAs(output);
        output.Position = 0;
        return output;
    }
}
