/**
 * Structure actions for the table family. Each "Add" affordance acts on its own
 * container: a row adds a cell to itself, a section adds a row to itself, the
 * table adds a section. All operate on the block tree (the single source of
 * truth) via fresh reads at click time.
 */

import { useRegistry } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { useCallback } from '@wordpress/element';
import {
	createTableCellBlock,
	createTableRowBlock,
	createTableSectionBlock,
} from './utils';

// Widest row (by cell count) in a section — the column count a new row should
// match so the section stays rectangular.
const sectionColumnCount = ( sel, sectionId ) => {
	let columns = 0;
	sel.getBlockOrder( sectionId ).forEach( ( rowId ) => {
		columns = Math.max( columns, sel.getBlockOrder( rowId ).length );
	} );
	return Math.max( 1, columns );
};

const rowCellTag = ( sel, rowId ) => {
	const cellId = sel.getBlockOrder( rowId )[ 0 ];
	return sel.getBlockAttributes( cellId )?.tagName || 'td';
};

const sectionCellTag = ( sel, sectionId ) => {
	const rowId = sel.getBlockOrder( sectionId )[ 0 ];
	return rowId ? rowCellTag( sel, rowId ) : 'td';
};

const tableColumnCount = ( sel, tableId ) => {
	let columns = 0;
	sel.getBlockOrder( tableId ).forEach( ( sectionId ) => {
		columns = Math.max( columns, sectionColumnCount( sel, sectionId ) );
	} );
	return Math.max( 1, columns );
};

// Append one cell to this row.
export function useAddCell( rowClientId ) {
	const registry = useRegistry();
	return useCallback( () => {
		const sel = registry.select( blockEditorStore );
		if ( ! rowClientId ) {
			return;
		}
		const at = sel.getBlockOrder( rowClientId ).length;
		registry
			.dispatch( blockEditorStore )
			.insertBlocks(
				createTableCellBlock( { tag: rowCellTag( sel, rowClientId ) } ),
				at,
				rowClientId,
				false
			);
	}, [ registry, rowClientId ] );
}

// Append one row (matching this section's column count) to this section.
export function useAddRow( sectionClientId ) {
	const registry = useRegistry();
	return useCallback( () => {
		const sel = registry.select( blockEditorStore );
		if ( ! sectionClientId ) {
			return;
		}
		const columns = sectionColumnCount( sel, sectionClientId );
		const tag = sectionCellTag( sel, sectionClientId );
		const at = sel.getBlockOrder( sectionClientId ).length;
		registry
			.dispatch( blockEditorStore )
			.insertBlocks(
				createTableRowBlock( { columns, tag } ),
				at,
				sectionClientId,
				false
			);
	}, [ registry, sectionClientId ] );
}

// Append one tbody section (matching the table's column count) to this table.
export function useAddSection( tableClientId ) {
	const registry = useRegistry();
	return useCallback( () => {
		const sel = registry.select( blockEditorStore );
		if ( ! tableClientId ) {
			return;
		}
		const columns = tableColumnCount( sel, tableClientId );
		const at = sel.getBlockOrder( tableClientId ).length;
		registry
			.dispatch( blockEditorStore )
			.insertBlocks(
				createTableSectionBlock( {
					tagName: 'tbody',
					rows: 1,
					columns,
				} ),
				at,
				tableClientId,
				false
			);
	}, [ registry, tableClientId ] );
}
