import { parseArgs } from "../lib/args.js";
import { resolveServer } from "../lib/client.js";
import { streamSse } from "../lib/sse.js";
import { dataUriToFile, defaultOutName, fileToDataUri } from "../lib/files.js";
import { out, die, color, json, exitCodeForError } from "../lib/output.js";
import { config } from "../../config.js";
import { createCliRequestId, recoverGeneratedOutputs, formatRecoveryHint } from "../lib/recover-output.js";
const SPEC = {
    flags: {
        quality: { short: "q", type: "string", default: "low" },
        size: { short: "s", type: "string", default: "1024x1024" },
        "max-images": { type: "string", default: "4" },
        out: { short: "o", type: "string" },
        "out-dir": { short: "d", type: "string" },
        json: { type: "boolean" },
        timeout: { type: "string", default: "600" },
        server: { type: "string" },
        model: { type: "string" },
        provider: { type: "string" },
        mode: { type: "string", default: "auto" },
        ref: { type: "string", repeatable: true },
        "reasoning-effort": { type: "string" },
        "web-search": { type: "boolean" },
        "no-web-search": { type: "boolean" },
        moderation: { type: "string", default: "low" },
        session: { type: "string" },
        "show-partial": { type: "boolean" },
        help: { short: "h", type: "boolean" },
    },
};
const HELP = `
  ima2 multimode <prompt...> [options]

  Stream multi-image generation via SSE (phase / partial / image / done / error).

  Options:
    -q, --quality <low|medium|high>     Default: low
    -s, --size <WxH>                    Default: 1024x1024
        --max-images <1..8>             Default: 4
    -o, --out <file>                    First image (implies --max-images 1)
    -d, --out-dir <dir>                 Output dir for multiple images
        --json
        --model <gpt-5.5|gpt-5.4|gpt-5.4-mini|grok-imagine-image|grok-imagine-image-quality|nano-banana-2|nano-banana-pro>
        --provider <auto|oauth|api|grok|grok-api|agy|gemini-api>
                                      Provider (oauth = GPT OAuth; grok = xAI Grok; agy/gemini-api = Gemini)
        --mode <auto|direct>            Prompt handling mode. Default: auto
        --ref <file>                    Attach reference image (repeatable, max 5)
        --reasoning-effort <none|low|medium|high|xhigh>
        --web-search / --no-web-search
        --moderation <auto|low>
        --session <id>
        --show-partial                  Print [partial #N received] notices
        --timeout <sec>                 Default: 600
`;
export default async function multimodeCmd(argv) {
    const args = parseArgs(argv, SPEC);
    if (args.help) {
        out(HELP);
        return;
    }
    const prompt = args.positional.join(" ");
    if (!prompt)
        die(2, "prompt required");
    const VALID_PROVIDERS = new Set(["auto", "oauth", "api", "grok", "grok-api", "agy", "gemini-api"]);
    const VALID_MODES = new Set(["auto", "direct"]);
    const VALID_REASONING = new Set(["none", "low", "medium", "high", "xhigh"]);
    if (args.provider && !VALID_PROVIDERS.has(String(args.provider))) {
        die(2, "--provider must be one of: auto, oauth, api, grok, grok-api, agy, gemini-api");
    }
    if (!VALID_MODES.has(String(args.mode)))
        die(2, "--mode must be one of: auto, direct");
    if (args["reasoning-effort"] && !VALID_REASONING.has(String(args["reasoning-effort"]))) {
        die(2, "--reasoning-effort must be one of: none, low, medium, high, xhigh");
    }
    if (args["web-search"] && args["no-web-search"]) {
        die(2, "--web-search and --no-web-search are mutually exclusive");
    }
    let server;
    try {
        server = await resolveServer({ serverFlag: args.server });
    }
    catch (e) {
        die(exitCodeForError(e), e.message);
        throw e;
    }
    const maxImages = Math.max(1, Math.min(8, parseInt(String(args["max-images"])) || 4));
    const refs = (Array.isArray(args.ref) ? args.ref : []);
    if (refs.length > 5)
        die(2, "max 5 --ref attachments");
    const references = await Promise.all(refs.map((p) => fileToDataUri(p)));
    const outDir = args["out-dir"] ? String(args["out-dir"]) : null;
    const explicitOut = args.out ? String(args.out) : null;
    const requestId = createCliRequestId("req_cli_multimode");
    const timeoutMs = (parseInt(String(args.timeout)) || 600) * 1000;
    const body = {
        prompt,
        quality: args.quality,
        size: args.size,
        maxImages,
        mode: args.mode,
        references,
        moderation: args.moderation,
        sessionId: args.session,
        requestId,
    };
    if (args.model)
        body.model = args.model;
    if (args.provider)
        body.provider = args.provider;
    if (args["reasoning-effort"])
        body.reasoningEffort = args["reasoning-effort"];
    if (args["no-web-search"])
        body.webSearchEnabled = false;
    else if (args["web-search"])
        body.webSearchEnabled = true;
    const ac = new AbortController();
    let timedOut = false;
    const timeoutTimer = setTimeout(() => {
        timedOut = true;
        ac.abort();
    }, timeoutMs);
    const onSig = () => { ac.abort(); process.exit(130); };
    process.once("SIGINT", onSig);
    process.once("SIGTERM", onSig);
    const url = `${server.base}/api/generate/multimode`;
    const images = [];
    let doneInfo = null;
    try {
        for await (const ev of streamSse(url, { body, signal: ac.signal, headers: { "X-Request-Id": requestId } })) {
            switch (ev.event) {
                case "phase":
                    if (!args.json)
                        out(color.dim(`[phase] ${ev.data.phase} (max ${ev.data.maxImages ?? maxImages})`));
                    break;
                case "partial":
                    if (args["show-partial"] && !args.json) {
                        const len = (ev.data.image || "").length;
                        out(color.dim(`[partial #${ev.data.index}] (${len}B preview)`));
                    }
                    break;
                case "image":
                    images.push(ev.data);
                    if (!args.json)
                        out(color.green(`✓ image ${images.length}`));
                    break;
                case "done":
                    doneInfo = ev.data;
                    break;
                case "error":
                    die(1, `multimode error: ${ev.data.error || ev.data}${ev.data.code ? ` (${ev.data.code})` : ""}`);
            }
        }
    }
    catch (e) {
        const isTimeout = e.name === "TimeoutError" || (e.name === "AbortError" && timedOut);
        if (e.name === "AbortError" && !timedOut)
            return;
        if (isTimeout && (explicitOut || outDir)) {
            const result = await recoverGeneratedOutputs(server.base, requestId, {
                explicitOut,
                outDir,
                expectedCount: maxImages,
                json: Boolean(args.json),
            });
            if (result.recovered) {
                if (args.json) {
                    json({ ok: true, requestId, recovered: true, paths: result.paths });
                }
                else {
                    out(formatRecoveryHint(result));
                    for (const p of result.paths)
                        out(color.green("✓ ") + p + color.dim(" (recovered)"));
                }
                return;
            }
            if (!args.json)
                out(formatRecoveryHint(result));
        }
        die(exitCodeForError(e), `${e.message}${e.code ? ` (${e.code})` : ""}`);
    }
    finally {
        clearTimeout(timeoutTimer);
        process.off("SIGINT", onSig);
        process.off("SIGTERM", onSig);
    }
    // Save images
    if (explicitOut && images.length > 1) {
        if (!args.json)
            out(color.yellow(`(received ${images.length} images, --out only saves first)`));
    }
    const savedPaths = [];
    for (let i = 0; i < images.length; i++) {
        const im = images[i];
        if (!im.image)
            continue;
        let target;
        if (explicitOut && i === 0)
            target = explicitOut;
        else if (outDir)
            target = `${outDir}/${defaultOutName(i, images.length)}`;
        else
            target = `${config.storage.generatedDir}/${defaultOutName(i, images.length)}`;
        if (target) {
            await dataUriToFile(im.image, target);
            savedPaths.push(target);
        }
    }
    if (args.json) {
        json({ ok: true, requestId: doneInfo?.requestId, returned: doneInfo?.returned, paths: savedPaths });
    }
    else {
        for (const p of savedPaths)
            out(color.green("✓ ") + p);
        if (doneInfo)
            out(color.dim(`done: ${doneInfo.returned}/${doneInfo.requested ?? maxImages}`));
    }
}
