import { $, ts } from "../../ir/builders.js";
/**
 * Encapsulates the construction of assignment LHS nodes and assignment
 * statements. This is the boring half of assignment lowering — building
 * `obj.foo[i] = value` style nodes from a scope + variable name + access
 * chain.
 *
 * The interesting / stateful half (handling llm calls, interrupts, async
 * function calls, message threads, etc.) stays in TypeScriptBuilder
 * because it touches most other subsystems; extracting it would not
 * meaningfully reduce coupling.
 *
 * Public API:
 *   - {@link scopedAssign} — build a full assignment statement
 *     (`lhs = value`, or `__ctx.globals.set(...)` for globals, or
 *     `.splice(...)` for slice assignment)
 *   - {@link lhs}          — build just the assignment target node
 *   - {@link accessChain}  — apply an access chain to an arbitrary base
 *     expression (used for `this.field = value` lowering)
 */
export class AssignmentEmitter {
    deps;
    constructor(deps) {
        this.deps = deps;
    }
    /**
     * Assign a value to a scoped variable.
     *
     * - Global scope, no access chain → `__ctx.globals.set(moduleId, name, value)`
     * - Slice as the last access element → array `.splice(...)` call
     * - Otherwise → ordinary `lhs = value`
     */
    scopedAssign(scope, varName, value, accessChain) {
        if (accessChain &&
            accessChain.length > 0 &&
            accessChain[accessChain.length - 1].kind === "slice") {
            return this.sliceAssign(scope, varName, value, accessChain);
        }
        if (scope === "global" && (!accessChain || accessChain.length === 0)) {
            return ts.globalSet(this.deps.moduleId, varName, value);
        }
        return ts.assign(this.lhs(scope, varName, accessChain), value);
    }
    /** Build the assignment target node (no value) for `scope.varName.chain`. */
    lhs(scope, variableName, chain) {
        return this.accessChain(ts.scopedVar(variableName, scope, this.deps.moduleId), chain);
    }
    /**
     * Apply an access chain (`.foo`, `[i]`, `.method(args)`) to an
     * arbitrary base expression. Used both for ordinary LHS construction
     * and for `this.field = value` / `super.field = value` lowering where
     * the base is a bare identifier rather than a scoped variable.
     */
    accessChain(base, chain) {
        if (!chain || chain.length === 0)
            return base;
        let result = base;
        for (const el of chain) {
            switch (el.kind) {
                case "property":
                    result = ts.prop(result, el.name);
                    break;
                case "index":
                    result = ts.index(result, this.deps.processNode(el.index));
                    break;
                case "methodCall": {
                    const fnCall = el.functionCall;
                    const config = this.deps.buildStateConfig();
                    const callArgs = [
                        result,
                        ts.str(fnCall.functionName),
                        this.deps.buildCallDescriptor(fnCall),
                    ];
                    if (config)
                        callArgs.push(config);
                    const callExpr = ts.call(ts.id("__callMethod"), callArgs);
                    result = ts.await(callExpr);
                    break;
                }
            }
        }
        return result;
    }
    /**
     * Slice assignment lowering:
     *
     *   arr[1:3] = [10, 20]  →  arr.splice(1, 3 - 1, ...[10, 20])
     *   arr[2:]  = [10]      →  arr.splice(2, arr.length - 2, ...[10])
     */
    sliceAssign(scope, varName, value, accessChain) {
        const sliceEl = accessChain[accessChain.length - 1];
        const baseChain = accessChain.length > 1 ? accessChain.slice(0, -1) : undefined;
        const base = this.lhs(scope, varName, baseChain);
        const startNode = sliceEl.start ? this.deps.processNode(sliceEl.start) : ts.raw("0");
        const endNode = sliceEl.end
            ? this.deps.processNode(sliceEl.end)
            : ts.prop(base, "length");
        const deleteCount = ts.binOp(endNode, "-", startNode);
        return $(base)
            .prop("splice")
            .call([startNode, deleteCount, ts.spread(value)])
            .done();
    }
}
