'use client';
import type { ReactElement } from 'react';
import { TERRITORIES } from '@/game/board';
import {
parseMovementExpression, MOVEMENT_EXPRESSION_MAX_DEPTH, MOVEMENT_EXPRESSION_MAX_LEAVES,
type MovementExpression,
} from '@/game/movement-expression-promises';
import { Button } from './ui/button';
import { Input } from './ui/input';
type TerritoryChoice = { id: string; name: string };
export function movementExpressionInputError(value: unknown): string | null {
try { parseMovementExpression(value); return null; }
catch (error) { return error instanceof Error ? error.message : 'Choose a valid ground-movement expression.'; }
}
// Structural edits preserve repairable destination and numeric drafts. The
// detached valid placeholders are used only to check tree shape and limits.
const structuralDraft = (node: MovementExpression): MovementExpression => node.kind === 'move'
? { kind: 'move', territory: 'carthag', minimum: 1 }
: { ...node, terms: node.terms.map(structuralDraft) };
const inputSize = (node: MovementExpression): { leaves: number; depth: number } => {
if (node.kind === 'move') return { leaves: 1, depth: 0 };
const sizes = node.terms.map(inputSize);
return { leaves: sizes.reduce((sum, child) => sum + child.leaves, 0), depth: 1 + Math.max(0, ...sizes.map(child => child.depth)) };
};
/** Hook-free controlled tree. The server decides future route feasibility. */
export function MovementExpressionFields({ value, territories, disabled, onChange }: {
value: MovementExpression;
territories: readonly TerritoryChoice[];
disabled: boolean;
onChange: (expression: MovementExpression) => void;
}) {
const newLeaf = (): MovementExpression => ({ kind: 'move', territory: territories[0]?.id ?? 'carthag', minimum: 1 });
const size = inputSize(value), error = movementExpressionInputError(value);
const commit = (next: MovementExpression) => {
if (!disabled && !movementExpressionInputError(structuralDraft(next))) onChange(next);
};
const render = (node: MovementExpression, path: string, replace: (next: MovementExpression) => MovementExpression,
remove?: () => MovementExpression): ReactElement => {
const id = `truth-movement-expression-${path}`;
const allowed = (next: MovementExpression) => !disabled && !movementExpressionInputError(structuralDraft(replace(next)));
const change = (next: MovementExpression) => commit(replace(next));
const wrap = (kind: 'and' | 'or'): MovementExpression => ({ kind, terms: [node, newLeaf()] });
return
;
};
return ;
}