/**
 * Pure HowTo step list helpers (insert / edit / remove / reorder).
 */

import { createItemId, isValidUuid } from './uuid';

/**
 * @return {{id:string,name:string,text:string,imageId:number}} Empty step.
 */
export function createEmptyStep() {
	return {
		id: createItemId(),
		name: '',
		text: '',
		imageId: 0,
	};
}

/**
 * Plain text from step rich text.
 *
 * @param {string} html Rich HTML.
 * @return {string} Plain.
 */
export function stepPlainText(html) {
	return String(html || '')
		.replace(/<[^>]+>/g, ' ')
		.replace(/\s+/g, ' ')
		.trim();
}

/**
 * Ensure every step has a stable UUID without rewriting existing valid ids.
 *
 * @param {Array<Object>} steps Raw steps.
 * @return {Array<{id:string,name:string,text:string,imageId:number}>} Normalized.
 */
export function ensureStableIds(steps) {
	const list = Array.isArray(steps) ? steps : [];
	const seen = new Set();
	return list.map((step) => {
		const name = step && typeof step.name === 'string' ? step.name : '';
		const text = step && typeof step.text === 'string' ? step.text : '';
		let imageId = 0;
		if (step && (typeof step.imageId === 'number' || typeof step.imageId === 'string')) {
			const parsed = parseInt(String(step.imageId), 10);
			imageId = Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
		}
		let id = step && typeof step.id === 'string' ? step.id.trim().toLowerCase() : '';
		if (!isValidUuid(id) || seen.has(id)) {
			id = createItemId();
		}
		seen.add(id);
		return { id, name, text, imageId };
	});
}

/**
 * @param {Array<Object>} steps Current steps.
 * @return {Array<Object>} Steps with one empty row appended.
 */
export function insertStep(steps) {
	return [...ensureStableIds(steps), createEmptyStep()];
}

/**
 * @param {Array<Object>} steps Current steps.
 * @param {number}        index Index to update.
 * @param {Object}        patch Partial fields.
 * @return {Array<Object>} Next steps.
 */
export function updateStepAt(steps, index, patch) {
	const list = ensureStableIds(steps);
	if (index < 0 || index >= list.length) {
		return list;
	}
	const next = { ...list[index] };
	if (Object.prototype.hasOwnProperty.call(patch, 'name')) {
		next.name = String(patch.name ?? '');
	}
	if (Object.prototype.hasOwnProperty.call(patch, 'text')) {
		next.text = String(patch.text ?? '');
	}
	if (Object.prototype.hasOwnProperty.call(patch, 'imageId')) {
		const parsed = parseInt(String(patch.imageId ?? 0), 10);
		next.imageId = Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
	}
	if (
		Object.prototype.hasOwnProperty.call(patch, 'id') &&
		isValidUuid(String(patch.id || ''))
	) {
		next.id = String(patch.id).trim().toLowerCase();
	}
	return list.map((step, i) => (i === index ? next : step));
}

/**
 * @param {Array<Object>} steps Current steps.
 * @param {number}        index Index to remove.
 * @return {Array<Object>} Next steps.
 */
export function removeStepAt(steps, index) {
	const list = ensureStableIds(steps);
	if (index < 0 || index >= list.length) {
		return list;
	}
	return list.filter((_, i) => i !== index);
}

/**
 * Move a step from one index to another.
 *
 * @param {Array<Object>} steps     Current steps.
 * @param {number}        fromIndex Source index.
 * @param {number}        toIndex   Destination index.
 * @return {Array<Object>} Reordered steps (ids preserved).
 */
export function reorderSteps(steps, fromIndex, toIndex) {
	const list = ensureStableIds(steps);
	if (
		fromIndex < 0 ||
		toIndex < 0 ||
		fromIndex >= list.length ||
		toIndex >= list.length ||
		fromIndex === toIndex
	) {
		return list;
	}
	const next = [...list];
	const [moved] = next.splice(fromIndex, 1);
	next.splice(toIndex, 0, moved);
	return next;
}

/**
 * Steps that have any visible content (kept in saved markup).
 *
 * @param {Array<Object>} steps Steps.
 * @return {Array<Object>} Visible steps.
 */
export function getVisibleSteps(steps) {
	return ensureStableIds(steps).filter((step) => {
		const name = (step.name || '').trim();
		const plain = stepPlainText(step.text);
		return name !== '' || plain !== '';
	});
}

/**
 * Complete steps (name or text filled) — schema emission candidates.
 *
 * @param {Array<Object>} steps Steps.
 * @return {Array<Object>} Complete steps.
 */
export function getCompleteSteps(steps) {
	return getVisibleSteps(steps);
}
