import ExcelJS from "exceljs";
import type {
	ImportedTransaction,
	ImportStatement,
} from "@/shared/lib/import/types";

/**
 * Converte serial number do Excel (1900 date system) para ano/mês/dia.
 * Excel trata 1900 como bissexto (serial 60 = 29/02/1900 inexistente).
 */
function excelSerialToDate(
	serial: number,
): { y: number; m: number; d: number } | null {
	if (serial < 1) return null;
	let adjusted = serial;
	if (serial > 60) adjusted--;
	const baseDate = Date.UTC(1899, 11, 31);
	const date = new Date(baseDate + adjusted * 86400000);
	return {
		y: date.getUTCFullYear(),
		m: date.getUTCMonth() + 1,
		d: date.getUTCDate(),
	};
}

function parseDateValue(value: unknown): string | null {
	if (value == null || value === "") return null;

	// Excel date serial number
	if (typeof value === "number") {
		const date = excelSerialToDate(value);
		if (!date) return null;
		const y = date.y;
		const m = String(date.m).padStart(2, "0");
		const d = String(date.d).padStart(2, "0");
		return `${y}-${m}-${d}`;
	}

	// ExcelJS pode retornar Date objects
	if (value instanceof Date) {
		const y = value.getUTCFullYear();
		const m = String(value.getUTCMonth() + 1).padStart(2, "0");
		const d = String(value.getUTCDate()).padStart(2, "0");
		return `${y}-${m}-${d}`;
	}

	const str = String(value).trim();

	// DD/MM/YYYY
	const dmyMatch = str.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
	if (dmyMatch) {
		return `${dmyMatch[3]}-${dmyMatch[2].padStart(2, "0")}-${dmyMatch[1].padStart(2, "0")}`;
	}

	// YYYY-MM-DD
	const isoMatch = str.match(/^(\d{4})-(\d{2})-(\d{2})$/);
	if (isoMatch) return str;

	return null;
}

function parseAmountValue(value: unknown): number | null {
	if (value == null || value === "") return null;
	if (typeof value === "number") return Math.abs(value);
	const num = Number.parseFloat(
		String(value)
			.replace(",", ".")
			.replace(/[^\d.-]/g, ""),
	);
	return Number.isNaN(num) ? null : Math.abs(num);
}

export async function parseXls(buffer: ArrayBuffer): Promise<ImportStatement> {
	const workbook = new ExcelJS.Workbook();
	await workbook.xlsx.load(buffer);

	if (workbook.worksheets.length === 0) {
		throw new Error("Arquivo sem abas.");
	}

	const sheet = workbook.worksheets[0];

	if (!sheet || sheet.rowCount < 2) {
		throw new Error(
			`Planilha vazia ou sem dados (${sheet?.rowCount ?? 0} linha(s) encontrada(s)).`,
		);
	}

	const transactions: ImportedTransaction[] = [];

	sheet.eachRow((row, rowNumber) => {
		if (rowNumber === 1) return; // skip header

		// ExcelJS row.values é 1-indexed (values[0] é undefined)
		const values = row.values as unknown[];
		const date = parseDateValue(values[1]);
		const description = values[2] != null ? String(values[2]).trim() : "";
		const amount = parseAmountValue(values[3]);
		const typeRaw =
			values[4] != null ? String(values[4]).toLowerCase().trim() : "";
		const transactionType = typeRaw === "receita" ? "income" : "expense";
		const categoryRaw = values[5] != null ? String(values[5]).trim() : null;

		if (!date || !description || amount === null || amount <= 0) return;

		transactions.push({
			externalId: null,
			externalIdOccurrence: 0,
			date,
			amount,
			description,
			sourceDescription: description,
			transactionType,
			categoryRaw,
		});
	});

	if (transactions.length === 0) {
		throw new Error("Nenhuma transação válida encontrada na planilha.");
	}

	const dates = transactions.map((t) => t.date).sort();
	const period = { from: dates[0], to: dates[dates.length - 1] };

	return {
		source: "Planilha",
		accountNumber: null,
		period,
		isCreditCard: false,
		transactions,
	};
}

export async function generateXlsTemplate(): Promise<ArrayBuffer> {
	const workbook = new ExcelJS.Workbook();
	const ws = workbook.addWorksheet("Lançamentos");

	ws.addRows([
		["Data", "Descrição", "Valor", "Tipo", "Categoria"],
		["01/03/2026", "Ingressos São Januário", 160, "despesa", "Lazer"],
		["01/03/2026", "Salário", 3000.0, "receita", "Salário"],
		["01/03/2026", "Posto do Vasco da Gama", 89.9, "despesa", "Transporte"],
	]);

	ws.getColumn(1).width = 14;
	ws.getColumn(2).width = 32;
	ws.getColumn(3).width = 12;
	ws.getColumn(4).width = 10;
	ws.getColumn(5).width = 24;

	// Dropdown para coluna Tipo (D2:D100)
	for (let i = 2; i <= 100; i++) {
		ws.getCell(`D${i}`).dataValidation = {
			type: "list",
			allowBlank: true,
			formulae: ['"despesa,receita"'],
		};
	}

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