let table      = null;
let period     = null;
let columns    = null;
let subcolumns = null;
let tblcolumns = null;
let rows       = null;
let subrows    = null;
let options    = {};
// let mouse      = "drag";

const form			= document.getElementById('report_options');
const title_el		= document.getElementById('report_title');
const title_contain	= document.getElementById('title_container');
const progress		= document.getElementById('report_progress');
const report_status	= document.getElementById('report_status');
const dt			= document.getElementById('datatable');
const searchbar		= document.getElementById('filter');
//const tool_select	= document.getElementById('tool_select');
const export_excel	= document.getElementById('export_excel');
const btn_print		= document.getElementById('btn_print');
const btn_link		= document.getElementById('create_link');
const btn_expand	= document.getElementById('btn_expand');
const btn_collapse	= document.getElementById('btn_collapse');

let report_title	= title_el.textContent;
let full_title		= report_title;
let expandedNested  = new Set();
let now             = new Date();
let tblWidth        = 0;

const sendOptions = async () => {
	const formData = new FormData(form);

	try {
		const response = await fetch(window.location.href, {
			method: "POST",
			body: formData
		});
		result = await response.json();
		if (result.status == 'ok') {
			//console.log(result);
			now = new Date();
			renderReport(result);
			progress.style.width = "100%";
		}
		else {
			progress.style.width = "0%";
			report_status.textContent = "Invalid options selected.";
			for (error in result.errors) {
				document.getElementById(`${error}:error`).textContent = result.errors[error];
			}
		}
	} catch (e) {
		progress.style.width = "0%";
		report_status.textContent = "Could not contact server.";
		console.error(e);
	}
}

const processColumns = (_columns) => {
	let result = [];
	_columns.forEach((item, index) => {
		let column = {title: item.name, field: index.toString()};
		if ('footer' in item) {
			column.minWidth = 130;
			if (item.footer === "sum") {
				column.bottomCalc = "sum";
			}
			else if (item.footer === "margin") {
				column.bottomCalc = (values, data, calcParams) => {
					const sales = data.reduce((acc, row) => acc + row[item.margin[0]], 0);
					if (sales == 0) {
						return '';
					}
					const costs = data.reduce((acc, row) => acc + row[item.margin[1]], 0);
					
					return 1 - costs/sales;
				};
			}
			else if (item.footer == "count") {
				column.bottomCalc = (values, data, calcParams) => {
					//console.log(values);
					const counts = {};
					for (const value of values) {
						counts[value] = counts[value] ? counts[value] + 1 : 1;
					}
					return `${counts['Order'] ?? 0} order${counts['Order'] == 1 ? '' : 's'}, ${counts['Quote'] ?? 0} quote${counts['Quote'] == 1 ? '' : 's'}`
				};
			}
		}
		if ('format' in item) {
			if (item.format === "int") {
				column.formatter = intFormatter;
				column.bottomCalcFormatter = intFormatter;
				column.hozAlign = "right";
			}
			else if (item.format === "$") {
				column.formatter = "money";
				column.bottomCalcFormatter = "money";
				column.hozAlign = "right";
			}
			else if (item.format === "%") {
				column.formatter = percentFormatter;
				column.bottomCalcFormatter = percentFormatter;
				column.hozAlign = "right";
			}
			else if (item.format === "textarea") {
				column.formatter = "textarea";
			}
		}
		if ('frozen' in item) {
			column.frozen = true;
		}
		result.push(column);
	});
	return result;
}

const renderReport = (result) => {
	report_title = result.title;
	period = result.period;
	columns = result.columns;
	subcolumns = result.subcolumns;
	tblcolumns = processColumns(result.columns);
	rows = result.rows;
	subrows = result.subrows;
	options = result.options;
	let subrowcount = 0;

	if (period) {
		full_title = `${report_title} for ${period}`;
		title_el.textContent = full_title;
	}

	progress.style.width = "60%";
	
	if (table !== null) { table.destroy(); }
	tblWidth = title_contain.offsetWidth - 50;

	let tableOptions = {
		columns: tblcolumns,
		data: rows,
		importFormat: "array",
		layout: "fitDataFill",
		selectableRows: false,
		movableColumns: true,
		//height: 300,
		printRowRange: "all",
		columnDefaults: {resizable: "header"}
	}

	if (options.group_by != null) {
		if (typeof(options.group_by) == 'number') {
			tableOptions.groupBy = options.group_by.toString();
		}
		else if (Array.isArray(options.group_by)) {
			tableOptions.groupBy = Array.from(options.group_by.map(x => x.toString()));
		}
		// else {
		// 	tableOptions.groupBy = '0';
		// }
		
		tableOptions.groupToggleElement = "header";
		tableOptions.columnCalcs = "both";
		tableOptions.groupClosedShowCalcs = true;
		document.getElementById("group-controls").classList.remove('d-none');
	}
	else {
		document.getElementById("group-controls").classList.add('d-none');
	}

	if (result.subcolumns) {
		for (let subrow in result.subrows) {
			subrowcount += result.subrows[subrow].length;
		}

		tableOptions.rowFormatter = (row) => {
			let key = row.getCell("0").getValue();
			if (key in result.subrows) {
				let holderEl = document.createElement("div");
				holderEl.classList.add('pb-2', 'ps-4');
				holderEl.style.maxWidth = tblWidth + 'px';

				let linkEl = document.createElement("a");
				linkEl.href = '#';
				let count = result.subrows[key].length;
				let text = count == 1 ? "1 entry" : `${count} entries`;
				
				holderEl.appendChild(linkEl);

				if (expandedNested.has(key)) {
					linkEl.innerHTML = '<i class="bi bi-caret-down me-1"></i>' + text;
					linkEl.addEventListener('click', (event) => {
						expandedNested.delete(key);
						row.reformat();
					});

					let tableEl = document.createElement("div");
					holderEl.appendChild(tableEl);
					row.getElement().appendChild(holderEl);

					let subTableOptions = {
						columns: processColumns(result.subcolumns),
						data: result.subrows[key],
						importFormat: "array",
						layout:"fitDataFill",
						responsiveLayout:"collapse",
						columnDefaults: {headerSort: false, resizable: false, responsive: 0},
						selectableRows: false,
						renderVertical:"basic"
					}
					subTableOptions.columns[4].responsive = 2;

					let subTable = new Tabulator(tableEl, subTableOptions);
				}
				else {
					linkEl.innerHTML = '<i class="bi bi-caret-right me-1"></i>' + text;
					linkEl.addEventListener('click', (event) => {
						expandedNested.add(key);
						row.reformat();
					});
					
					row.getElement().appendChild(holderEl);
				}
			}
		}

		tableOptions.rowFormatterPrint = false;

		// The virtual DOM renderer is buggy when the row height is large.
		tableOptions.renderVertical = 'basic';
	}

	report_status.textContent = `Showing ${rows.length}${subrowcount == 0 ? '' : '+'+subrowcount} record${rows.length == 1 ? '' : 's'}.`;

	table = new Tabulator("#datatable", tableOptions);
	table.on("dataLoaded", (data) => {
		//pointerScroll(document.getElementsByClassName("tabulator-tableholder")[0]);
		document.getElementById('table-controls').classList.remove('invisible');
	});

	searchbar.addEventListener("keyup", () => {
		let filters = [];

		table.getColumns().forEach((column) => {
			filters.push({
				field: column.getField(),
				type: "like",
				value: searchbar.value,
			});
		});
		table.setFilter([filters]);
	});
}

// const pointerScroll = (elem) => {
// 	const dragStart = (ev) => {
// 		if(mouse === "drag" && ev.button == 0 && ev.target.tagName != 'A' && !Array.from(document.getElementsByClassName('tabulator-group')).some((node) => {
// 			return node.contains(ev.target);
// 		})) {
// 			ev.preventDefault();
// 			elem.setPointerCapture(ev.pointerId);
// 		}
// 	}
// 	const dragEnd = (ev) => {
// 		if(mouse === "drag" && ev.button == 0) {
// 			elem.releasePointerCapture(ev.pointerId);
// 		}
// 	}
// 	const drag = (ev) => {
// 		ev.preventDefault();
// 		if(ev.buttons != 0 && elem.hasPointerCapture(ev.pointerId)) {
// 			elem.scrollLeft -= ev.movementX;
// 			elem.scrollTop -= ev.movementY;
// 		}
// 	}

// 	elem.addEventListener("pointerdown", dragStart);
// 	elem.addEventListener("pointerup", dragEnd);
// 	elem.addEventListener("pointermove", drag);
// };

const excelWidth = (width) => {
	return Math.floor((width * 7 + 5) / 7 * 256) / 256;
}

const excelFixColumns = (sheet, icolumns, irows, mainsheet=true) => {
	let rowskip = mainsheet ? 2 : 0;
	let colskip = mainsheet ? 0 : 2;

	for (let i = 0; i < icolumns.length+colskip; i++) {
		const column = sheet.getColumn(i+1);
		let maxLength = 10;
		for (let j = 1+rowskip; j <= irows.length+1+rowskip; j++) {
			if (column.values[j]) {
				maxLength = Math.max(maxLength, column.values[j].toString().length)
			}
		}
		column.width = excelWidth(maxLength + 2);

		if (i >= colskip && 'format' in icolumns[i-colskip]) {
			if (icolumns[i-colskip].format == "int") {
				column.numFmt = "0";
			}
			else if (icolumns[i-colskip].format == "$") {
				column.numFmt = "#,##0.00";
			}
			else if (icolumns[i-colskip].format == "%") {
				column.numFmt = "0.00%";
			}
		}
	}
}

const excelFormatter = async (list, _options, setFileContents) => {
	const workbook = new ExcelJS.Workbook();
	const sheet_name = report_title.replaceAll(/[\*\?\:\\\/\[\]]/g, ' ');

	if (options.formatting == "parker") {
		const todayDate = new Date();
		const lastMonth = new Date(todayDate.getFullYear(), todayDate.getMonth() - 1, 1);
		const reportMonth = String(lastMonth.getMonth()+1).padStart(2, '0');
		const reportYear = String(lastMonth.getFullYear()).slice(2);

		const sheet = workbook.addWorksheet("CEA");
		sheet.columns = [
			{ header: '*DFH', key: '0', width: excelWidth(4) },
			{ header: '292512', key: '1', width: excelWidth(10) },
			{ header: 'Custom Energized Air Ltd., Edmonton, AB', key: '2', width: excelWidth(3) },
			{ key: '3', width: excelWidth(25) },
			{ key: '4', width: excelWidth(18) },
			{ key: '5', width: excelWidth(2) },
			{ key: '6', width: excelWidth(5) },
			{ key: '7', width: excelWidth(9) },
			{ key: '8', width: excelWidth(4) },
			{ header: `${reportMonth}${reportYear}`, key: '9', width: excelWidth(20) },
			{ key: '10', width: excelWidth(6) },
			{ key: '11', width: excelWidth(4) },
			{ key: '12', width: excelWidth(10) },
		]

		for (let row of rows.toSorted( (a,b) => a[6].localeCompare(b[6]) )) {
			sheet.addRow(['*DDD', row[0], null, row[1], row[2], row[3], null, row[8], null, row[6], null, row[9]]);
		}
		sheet.addRow(['*DFT', '292512', '0000000000']);
	}
	else if (options.formatting) {
		let export_columns = [];
		columns.forEach(column => {
			let col_options = { name: column.name, filterButton: true }
			if ('footer' in column) {
				if (column.footer === "sum") {
					col_options.totalsRowFunction = 'sum';
				}
				else if (column.footer === 'margin') {
					col_options.totalsRowFunction = 'custom';
					let sales = `SUBTOTAL(109,[${columns[column.margin[0]].name}])`;
					let costs = `SUBTOTAL(109,[${columns[column.margin[1]].name}])`;
					col_options.totalsRowFormula = `IFERROR(1 - ${costs} / ${sales}, "")`;
				}
			}
			export_columns.push(col_options);
		});
		if (!('footer' in columns[0])) {
			export_columns[0].totalsRowLabel = 'Totals:';
		}

		const link = create_link(true);
		const sheet = workbook.addWorksheet(sheet_name, {
			views: [ {state: 'frozen', xSplit: 0, ySplit:3} ]
		});
		sheet.getCell('A1').value = `${full_title} - Ran ${now.toLocaleString('sv')}`;
		sheet.getCell('A2').value = { text: link, hyperlink: link };
		sheet.addTable({
			name: 'data',
			ref: 'A3',
			headerRow: true,
			totalsRow: true,
			style: { showRowStripes: true },
			columns: export_columns,
			rows: rows
		});

		excelFixColumns(sheet, columns, rows);

		if (subcolumns !== null) {
			let export_subcolumns = Array.from(subcolumns.map(column => {return {name: column.name, filterButton: true};}));
			export_subcolumns.unshift({name: columns[1].name, filterButton: true});
			export_subcolumns.unshift({name: columns[0].name, filterButton: true});

			let descriptions = rows.reduce((result, row) => {
				result[row[0]] = row[1];
				return result;
			}, {});

			const subsheet = workbook.addWorksheet('Detail', {
				views: [ {state: 'frozen', xSplit: 0, ySplit:1} ]
			});
			let flatrows = [];
			for (let key in subrows) {
				for (let subrow of subrows[key]) {
					flatrows.push([key, descriptions[key], ...subrow])
				}
			}
			if (flatrows.length == 0) {
				flatrows.push(['', '', '', '', '', '', '', '', '', '', ''])
			}
			subsheet.addTable({
				name: 'detail',
				ref: 'A1',
				headerRow: true,
				style: { showRowStripes: true },
				columns: export_subcolumns,
				rows: flatrows
			});

			if (options.formatting != "performance") {
				excelFixColumns(subsheet, subcolumns, flatrows, false);
			}
		}
	}
	else {
		const sheet = workbook.addWorksheet(sheet_name);
		sheet.columns = tblcolumns.map(col => { return {header: col.title, key: col.field} });
		for (let row of rows) {
			sheet.addRow(row);
		}
	}

	const buffer = await workbook.xlsx.writeBuffer();

	setFileContents(buffer, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}

const intFormatter = (cell, formatterParams, onRendered) => {
	if(typeof cell.getValue() === "undefined") {
		return '';
	}
	let num = parseInt(cell.getValue().toString(), 10);
	return isNaN(num) ? '' : num;
}
const percentFormatter = (cell, formatterParams, onRendered) => {
	if(typeof cell.getValue() === "undefined") {
		return '';
	}
	let num = parseFloat(cell.getValue().toString());
	return isNaN(num) ? '' : (num*100).toFixed(2)+'%';
}

const create_link = (everything) => {
	let base_url = window.location.href.split(/[?#]/)[0];
	let params = [];
	document.querySelectorAll("input[type=checkbox]").forEach( (input) => {
		if (input.checked) {
			params.push(input.id);
		}
	});
	document.querySelectorAll("input[type=radio]").forEach( (input) => {
		if (input.checked) {
			params.push(`${input.name}=${input.value}`);
		}
	});
	document.querySelectorAll("input[type=number]").forEach( (input) => {
		params.push(`${input.name}=${input.value}`);
	});
	if (everything) {
		document.querySelectorAll("input").forEach( (input) => {
			if (['date', 'month'].includes(input.type)) {
				params.push(`${input.name}=${input.value}`);
			}
			else if (input.type == 'text' && input.id != 'filter') {
				params.push(`${input.name}=${encodeURIComponent(input.value)}`);
			}
		});
	}

	params.push('run');
	return `${base_url}?${params.join('&')}`;
}

const checkall = (name) => {
	const group_checkboxes = [...document.querySelectorAll("input[type=checkbox]")]
		.filter( (input) => input.name.startsWith(name) );
	const state = group_checkboxes.every( (input) => input.checked );
	group_checkboxes.forEach( (input) => input.checked = !state );
}

const getSuggestions = async (query) => {
	try {
		const method = document.querySelector(`input[name=${suggest.getAttribute('suggest')}]:checked`).value;
		const formData = new FormData();
		formData.append("options", method);
		const response = await fetch('/suggest', {
			method: "POST",
			body: formData
		});
		return await response.json();
	} catch (error) {
		console.error(error);
	}
}

form.addEventListener("submit", (event) => {
	event.preventDefault();
	progress.style.width = "20%";
	report_status.textContent = "Retrieving data...";
	for (message_field of document.getElementsByClassName('error')) {
		message_field.textContent = ""
	}
	sendOptions();
});

if (document.getElementById('autorun').value == 'true') {
	sendOptions();
}

btn_link.addEventListener("click", (event) => {
	window.location = create_link(true);
});
// tool_select.addEventListener("click", (event) => {
// 	if (mouse == "drag") {
// 		mouse = "select";
// 		tool_select.textContent = "Mouse: Select";
// 		tool_select.classList.add("active");
// 		dt.childNodes[1].classList.add("selectable");
// 	}
// 	else if (mouse == "select") {
// 		mouse = "drag";
// 		tool_select.textContent = "Mouse: Drag";
// 		tool_select.classList.remove("active");
// 		dt.childNodes[1].classList.remove("selectable");
// 	}
// });
export_excel.addEventListener("click", (event) => {
	table.download(excelFormatter, `${full_title} - Ran ${now.toLocaleString('sv').replace(':', '-')}.xlsx`);
});
btn_print.addEventListener("click", (event) => {
	table.print();
});
btn_expand.addEventListener("click", (event) => {
	table.setGroupStartOpen(true);
	let original = table.options.groupBy;
	table.setGroupBy(false);
	table.setGroupBy(original);
});
btn_collapse.addEventListener("click", (event) => {
	table.setGroupStartOpen(false);
	let original = table.options.groupBy;
	table.setGroupBy(false);
	table.setGroupBy(original);
});

let suggest = document.querySelector('[suggest]');
if (suggest) {
	suggest.addEventListener("selection", (event) => {
		suggest.value = event.detail.selection.value.split(' - ')[0];
	});
	let config = {
		selector: () => { return suggest },
		data: {
			src: getSuggestions,
			cache: true
		},
		resultItem: { highlight: true },
		resultsList: {
			destination: '#searchresults',
			position: 'afterbegin',
			maxResults: 100,
			//noResults: true,
			tabSelect: true
		},
		wrapper: false
	};
	let autoCompleteJS = new autoComplete(config);
	document.querySelectorAll(`input[name=${suggest.getAttribute('suggest')}]`).forEach( (input) => {
		input.addEventListener("change", (radio) => {
			getSuggestions('').then((data) => {
				autoCompleteJS.data.store = data;
				autoCompleteJS.start(suggest.value);
			});
		});
	});
}