/**
 * parse-imports — pure function that extracts every `import` declaration
 * from a candidate code block as a flat list of per-binding entries.
 *
 * Output shape is intentionally flat: the W0198 preflight checks each
 * `(source, imported)` pair against the resolved package surface, so a
 * flat list is the natural input. Multi-binding declarations
 * (`import { a, b as c } from "pkg"`) produce one entry per binding;
 * default + named combos (`import def, { a } from "pkg"`) likewise.
 *
 * Implementation: delegates to `oxc-parser`'s `staticImports` view, which
 * already decomposes each import statement into entries with explicit
 * `importName` / `localName` / `isType` fields. The TS-aware grammar
 * means dynamic `import(...)`, `import.meta`, and malformed statements
 * are all handled by the parser itself — we just translate the entries
 * into our `CandidateImportBinding` shape.
 *
 * Recognized grammar (handled by oxc-parser):
 *   - `import { a, b as c } from "pkg"`
 *   - `import def from "pkg"`
 *   - `import * as ns from "pkg"`
 *   - `import def, { a } from "pkg"` and `import def, * as ns from "pkg"`
 *   - `import type { ... } from "pkg"` and `import { type a, b } from "pkg"`
 *   - `import "pkg"` (side-effect; surfaced as a single `side-effect` entry)
 *   - Multi-line variants of all of the above.
 *
 * Out of scope (intentionally — both regex and oxc-parser ignore these):
 *   - Dynamic `import("pkg")` — runtime, not statically resolvable here.
 *   - `export { a } from "pkg"` re-exports from candidate code.
 *   - TypeScript `import = require()` and `import("...").Type` ambient
 *     references — neither pattern shows up in the App SDK / Studio
 *     candidate corpus the preflight grades against.
 */
import { parseSync } from "oxc-parser";
export function parseImports(src) {
    // Use the `.tsx` filename hint so the parser tolerates JSX in candidate
    // code (App SDK literacy tasks frequently include JSX in their answers).
    const result = parseSync("input.tsx", src, { lang: "tsx" });
    const lineStarts = computeLineStarts(src);
    const offsetToLine = (offset) => binarySearchLine(lineStarts, offset) + 1;
    const out = [];
    for (const imp of result.module.staticImports) {
        const source = imp.moduleRequest.value;
        // The parser recovers from malformed specifiers (e.g. backtick-quoted
        // module requests) by emitting an empty source; the W0198 preflight
        // can't do anything useful with that, so drop those entries.
        if (!source)
            continue;
        const line = offsetToLine(imp.start);
        if (imp.entries.length === 0) {
            // Side-effect form: `import "pkg"`.
            out.push({
                source,
                kind: "side-effect",
                imported: "",
                local: "",
                isType: false,
                line,
            });
            continue;
        }
        for (const entry of imp.entries) {
            const local = entry.localName.value;
            const isType = entry.isType;
            switch (entry.importName.kind) {
                case "Name":
                    out.push({
                        source,
                        kind: "named",
                        imported: entry.importName.name ?? local,
                        local,
                        isType,
                        line,
                    });
                    break;
                case "Default":
                    out.push({
                        source,
                        kind: "default",
                        imported: "default",
                        local,
                        isType,
                        line,
                    });
                    break;
                case "NamespaceObject":
                    out.push({
                        source,
                        kind: "namespace",
                        imported: "*",
                        local,
                        isType,
                        line,
                    });
                    break;
            }
        }
    }
    return out;
}
// ---------------------------------------------------------------------------
// Line-number helpers — oxc-parser returns byte offsets; the W0198
// preflight surfaces 1-based line numbers in findings so reviewers can
// jump straight to the citing line.
// ---------------------------------------------------------------------------
function computeLineStarts(src) {
    const offsets = [0];
    for (let i = 0; i < src.length; i++) {
        if (src[i] === "\n")
            offsets.push(i + 1);
    }
    return offsets;
}
function binarySearchLine(offsets, target) {
    let lo = 0;
    let hi = offsets.length - 1;
    while (lo < hi) {
        const mid = (lo + hi + 1) >>> 1;
        if (offsets[mid] <= target)
            lo = mid;
        else
            hi = mid - 1;
    }
    return lo;
}
