// AST node definitions for the dashboard's hand-rolled Rhai parser.
//
// Every node carries `start` / `end` byte offsets into the source so the
// editor features (autocomplete, goto-def, find-usages, format) can map
// between positions in the document and nodes in the tree.
//
// The shape mirrors the Rhai book grammar (https://rhai.rs/book/language/)
// but simplified: type annotations are absent (Rhai is dynamic), and
// statement-vs-expression duality is collapsed by letting `if` / `switch` /
// block expressions appear in both positions (an `ExprStmt` wrapper turns
// any expression into a statement).

export interface Range {
	start: number;
	end: number;
}

// ---------------------------------------------------------------------------
// Comments — captured by the lexer with their positions and re-emitted by
// the formatter. Kept off the AST tree so they don't clutter walkers.
// ---------------------------------------------------------------------------

export interface Comment extends Range {
	kind: 'LineComment' | 'BlockComment';
	text: string;
}

// ---------------------------------------------------------------------------
// Statements
// ---------------------------------------------------------------------------

export type Stmt =
	| LetStmt
	| ConstStmt
	| FnDecl
	| ExprStmt
	| ReturnStmt
	| WhileStmt
	| LoopStmt
	| ForStmt
	| BreakStmt
	| ContinueStmt
	| TryStmt;

export interface LetStmt extends Range {
	kind: 'Let';
	name: string;
	nameRange: Range;
	init: Expr | null;
}

export interface ConstStmt extends Range {
	kind: 'Const';
	name: string;
	nameRange: Range;
	init: Expr | null;
}

export interface Param extends Range {
	name: string;
}

export interface FnDecl extends Range {
	kind: 'FnDecl';
	name: string;
	nameRange: Range;
	params: Param[];
	body: BlockExpr;
}

export interface ExprStmt extends Range {
	kind: 'ExprStmt';
	expr: Expr;
	// Whether the statement is terminated with `;`. Block-form expressions
	// (`if`/`switch`/`{...}`) don't require it; everything else does.
	semi: boolean;
}

export interface ReturnStmt extends Range {
	kind: 'Return';
	value: Expr | null;
}

export interface WhileStmt extends Range {
	kind: 'While';
	cond: Expr;
	body: BlockExpr;
}

export interface LoopStmt extends Range {
	kind: 'Loop';
	body: BlockExpr;
}

export interface ForStmt extends Range {
	kind: 'For';
	varName: string;
	varRange: Range;
	iter: Expr;
	body: BlockExpr;
}

export interface BreakStmt extends Range {
	kind: 'Break';
}

export interface ContinueStmt extends Range {
	kind: 'Continue';
}

export interface TryStmt extends Range {
	kind: 'Try';
	body: BlockExpr;
	catchVar: string | null;
	catchVarRange: Range | null;
	handler: BlockExpr;
}

// ---------------------------------------------------------------------------
// Expressions
// ---------------------------------------------------------------------------

export type Expr =
	| IdentExpr
	| NumberExpr
	| StringExpr
	| BoolExpr
	| NullExpr
	| CallExpr
	| MemberExpr
	| IndexExpr
	| UnaryExpr
	| BinaryExpr
	| AssignExpr
	| ParenExpr
	| ObjectMapExpr
	| ArrayExpr
	| FnExpr
	| IfExpr
	| SwitchExpr
	| BlockExpr;

export interface IdentExpr extends Range {
	kind: 'Ident';
	name: string;
}

export interface NumberExpr extends Range {
	kind: 'Number';
	raw: string;
}

export interface StringExpr extends Range {
	kind: 'String';
	// The surrounding quote — `"` is escape-processed, backtick is raw and
	// may span multiple lines. We don't decode escapes; the formatter just
	// preserves the raw text between the quotes.
	quote: '"' | '`';
	raw: string;
}

export interface BoolExpr extends Range {
	kind: 'Bool';
	value: boolean;
}

export interface NullExpr extends Range {
	kind: 'Null';
}

export interface CallExpr extends Range {
	kind: 'Call';
	callee: Expr;
	args: Expr[];
}

export interface MemberExpr extends Range {
	kind: 'Member';
	object: Expr;
	property: string;
	propertyRange: Range;
}

export interface IndexExpr extends Range {
	kind: 'Index';
	object: Expr;
	index: Expr;
}

export interface UnaryExpr extends Range {
	kind: 'Unary';
	op: string;
	operand: Expr;
}

export interface BinaryExpr extends Range {
	kind: 'Binary';
	op: string;
	left: Expr;
	right: Expr;
}

export interface AssignExpr extends Range {
	kind: 'Assign';
	op: string; // = += -= *= /= %= ??=
	target: Expr;
	value: Expr;
}

export interface ParenExpr extends Range {
	kind: 'Paren';
	expr: Expr;
}

export interface ObjectMapEntry extends Range {
	key: string;
	keyRange: Range;
	value: Expr;
}

export interface ObjectMapExpr extends Range {
	kind: 'ObjectMap';
	entries: ObjectMapEntry[];
}

export interface ArrayExpr extends Range {
	kind: 'Array';
	elements: Expr[];
}

export interface FnExpr extends Range {
	kind: 'FnExpr';
	params: Param[];
	body: BlockExpr;
}

export interface IfExpr extends Range {
	kind: 'IfExpr';
	cond: Expr;
	then: BlockExpr;
	// else branch: either a block or another `if` for `else if` chains.
	else_: BlockExpr | IfExpr | null;
}

export interface SwitchArm extends Range {
	pattern: Expr | null; // null = `_` default case
	guard: Expr | null;
	value: Expr;
}

export interface SwitchExpr extends Range {
	kind: 'SwitchExpr';
	subject: Expr;
	arms: SwitchArm[];
}

export interface BlockExpr extends Range {
	kind: 'BlockExpr';
	stmts: Stmt[];
}

// ---------------------------------------------------------------------------
// Top-level parse output
// ---------------------------------------------------------------------------

export interface ParseError extends Range {
	message: string;
}

export interface ParseResult {
	source: string;
	program: BlockExpr;
	errors: ParseError[];
	comments: Comment[];
	// Offsets at which the source contained a blank line (a whitespace
	// run with two or more newlines). One entry per blank run; the
	// formatter consults these to preserve user-intent vertical grouping.
	blankLines: number[];
}
