type ExcelJsModule = typeof import("exceljs");

type WorkbookCellValue = boolean | Date | number | string | null | undefined;
type WorkbookRow = WorkbookCellValue[];
type WorkbookSheet = {
  name: string;
  rows: WorkbookRow[];
};

let _excelJs: ExcelJsModule | null = null;

async function getExcelJS() {
  if (!_excelJs) {
    _excelJs = await import("exceljs");
  }
  return _excelJs;
}

export async function createWorkbookArrayBuffer(
  sheetName: string,
  rows: WorkbookRow[],
): Promise<ArrayBuffer> {
  return createWorkbookArrayBufferFromSheets([{ name: sheetName, rows }]);
}

export async function createWorkbookArrayBufferFromSheets(
  sheets: WorkbookSheet[],
): Promise<ArrayBuffer> {
  const ExcelJS = await getExcelJS();
  const workbook = new ExcelJS.Workbook();

  for (const sheet of sheets) {
    const worksheet = workbook.addWorksheet(sheet.name);
    for (const row of sheet.rows) {
      worksheet.addRow(row.map((value) => value ?? ""));
    }
  }

  const buffer = await workbook.xlsx.writeBuffer();
  return buffer as ArrayBuffer;
}

export async function downloadWorkbook(
  fileName: string,
  sheetName: string,
  rows: WorkbookRow[],
): Promise<void> {
  return downloadWorkbookSheets(fileName, [{ name: sheetName, rows }]);
}

export async function downloadWorkbookSheets(
  fileName: string,
  sheets: WorkbookSheet[],
): Promise<void> {
  const buffer = await createWorkbookArrayBufferFromSheets(sheets);
  const blob = new Blob([buffer], {
    type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  });
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = fileName;
  anchor.click();
  URL.revokeObjectURL(url);
}
