/** @import { Expression } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import * as b from '#compiler/builders';
import { create_derived } from '../utils.js';

/**
 * @param {AST.LetDirective} node
 * @param {ComponentContext} context
 */
export function LetDirective(node, context) {
	// let:x        -->  const x = $.derived(() => $$slotProps.x);
	// let:x={{y, z}}  -->  const derived_x = $.derived(() => { const { y, z } = $$slotProps.x; return { y, z }));
	if (node.expression && node.expression.type !== 'Identifier') {
		const name = context.state.scope.generate(node.name);
		const bindings = context.state.scope.get_bindings(node);

		for (const binding of bindings) {
			context.state.transform[binding.node.name] = {
				read: (node) => b.member(b.call('$.get', b.id(name)), node)
			};
		}

		context.state.let_directives.push(
			b.const(
				name,
				b.call(
					'$.derived',
					b.thunk(
						b.block([
							b.let(
								/** @type {Expression} */ (node.expression).type === 'ObjectExpression'
									? // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
										b.object_pattern(node.expression.properties)
									: // @ts-expect-error types don't match, but it can't contain spread elements and the structure is otherwise fine
										b.array_pattern(node.expression.elements),
								b.member(b.id('$$slotProps'), node.name)
							),
							b.return(b.object(bindings.map((binding) => b.init(binding.node.name, binding.node))))
						])
					)
				)
			)
		);
	} else {
		const name = node.expression === null ? node.name : node.expression.name;
		context.state.transform[name] = {
			read: (node) => b.call('$.get', node)
		};

		context.state.let_directives.push(
			b.const(name, create_derived(context.state, b.member(b.id('$$slotProps'), node.name)))
		);
	}
}
