const tableUtils = axe.commons.table;
const cells = tableUtils.getAllCells(node);
const checkResult = this;

// Get a list of all headers reffed to in this rule
let reffedHeaders = [];
cells.forEach(function(cell) {
	const headers = cell.getAttribute('headers');
	if (headers) {
		reffedHeaders = reffedHeaders.concat(headers.split(/\s+/));
	}

	const ariaLabel = cell.getAttribute('aria-labelledby');
	if (ariaLabel) {
		reffedHeaders = reffedHeaders.concat(ariaLabel.split(/\s+/));
	}
});

// Get all the headers
const headers = cells.filter(function(cell) {
	if (axe.commons.text.sanitize(cell.textContent) === '') {
		return false;
	}
	return (
		cell.nodeName.toUpperCase() === 'TH' ||
		['rowheader', 'columnheader'].indexOf(cell.getAttribute('role')) !== -1
	);
});

const tableGrid = tableUtils.toGrid(node);

let out = true;
headers.forEach(header => {
	if (
		header.getAttribute('id') &&
		reffedHeaders.includes(header.getAttribute('id'))
	) {
		return;
	}

	const pos = tableUtils.getCellPosition(header, tableGrid);

	// ensure column header has at least 1 non-header cell and that the cell is
	// not pointing to a different header
	let hasCell = false;
	if (tableUtils.isColumnHeader(header)) {
		hasCell = tableUtils
			.traverse('down', pos, tableGrid)
			.find(
				cell =>
					!tableUtils.isColumnHeader(cell) &&
					tableUtils.getHeaders(cell, tableGrid).includes(header)
			);
	}

	// ensure row header has at least 1 non-header cell and that the cell is not
	// pointing to a different header
	if (!hasCell && tableUtils.isRowHeader(header)) {
		hasCell = tableUtils
			.traverse('right', pos, tableGrid)
			.find(
				cell =>
					!tableUtils.isRowHeader(cell) &&
					tableUtils.getHeaders(cell, tableGrid).includes(header)
			);
	}

	// report the node as having failed
	if (!hasCell) {
		checkResult.relatedNodes(header);
	}

	out = out && hasCell;
});

return out ? true : undefined;
