/**
 * SAC multi-modal generation core logic
 */
import { existsSync, mkdirSync } from "node:fs";
import { join } from "node:path";
import { SACError, ExitCode } from "./errors.js";
import { requestJson } from "./client.js";
import { generationEndpoint } from "./endpoints.js";
import { pollTask } from "./poll.js";
import { downloadFile } from "./download.js";
import { getProvider, allModels, modelsByCategory, modelsByProvider, } from "./providers/registry.js";
export { allModels, modelsByCategory, modelsByProvider, getProvider };
export const DEFAULT_IMAGE_MODEL = "volces_seedream_4_5";
export const DEFAULT_VIDEO_MODEL = "volces_seedance_1_5_pro";
export const DEFAULT_AUDIO_MODEL = "lyria_3_pro_preview";
export const DEFAULT_3D_TEXT_MODEL = "tripo3d_text_to_model";
export const DEFAULT_3D_IMAGE_MODEL = "tripo3d_image_to_model";
export const DEFAULT_3D_MULTIVIEW_MODEL = "tripo3d_multiview_to_model";
function detectDefault3dModel(flags) {
    const prompt = typeof flags.prompt === "string" && flags.prompt.length > 0;
    const imageUrl = typeof flags.imageUrl === "string" && flags.imageUrl.length > 0;
    const imageUrls = Array.isArray(flags.imageUrls)
        ? flags.imageUrls.filter((value) => typeof value === "string" && value.length > 0)
        : [];
    if (imageUrls.length > 0 && !imageUrl && !prompt)
        return DEFAULT_3D_MULTIVIEW_MODEL;
    if (imageUrl && !prompt && imageUrls.length === 0)
        return DEFAULT_3D_IMAGE_MODEL;
    if (prompt && !imageUrl && imageUrls.length === 0)
        return DEFAULT_3D_TEXT_MODEL;
    return undefined;
}
function resolveDefaultModel(category, config, flags) {
    if (typeof flags.model === "string" && flags.model.length > 0) {
        return flags.model;
    }
    switch (category) {
        case "image":
            return config.defaultImageModel || DEFAULT_IMAGE_MODEL;
        case "video":
            return DEFAULT_VIDEO_MODEL;
        case "audio":
            return DEFAULT_AUDIO_MODEL;
        case "3d":
            return detectDefault3dModel(flags);
    }
}
function detect3dExtension(url) {
    const match = url.match(/\.([a-z0-9]+)(?:[?#].*)?$/i);
    const ext = match?.[1]?.toLowerCase();
    if (ext && ["glb", "obj", "stl", "usdz", "fbx", "mp4"].includes(ext)) {
        return ext;
    }
    return "glb";
}
function getPromptRequirementHint(category) {
    switch (category) {
        case "image":
            return "seawork sac image --prompt <text>";
        case "video":
            return "seawork sac video [--model <model>] --prompt <text>";
        case "audio":
            return "seawork sac audio [--model <model>] --prompt <text>";
        case "3d":
            return "seawork sac 3d [--model <model>] --prompt <text>";
    }
}
function getModelRequirementHint(category) {
    switch (category) {
        case "image":
            return "seawork sac image --prompt <text>";
        case "video":
            return "seawork sac video [--model <model>] --prompt <text>";
        case "audio":
            return "seawork sac audio [--model <model>] --prompt <text>";
        case "3d":
            return "seawork sac 3d [--model <model>] --prompt <text> | --image-url <url> | --image-urls <url>...";
    }
}
function getDefaultPrefix(category) {
    switch (category) {
        case "image":
            return "image";
        case "video":
            return "video";
        case "audio":
            return "audio";
        case "3d":
            return "model";
    }
}
function detectExtension(category, url) {
    if (category === "video")
        return "mp4";
    if (category === "audio")
        return url.includes(".wav") ? "wav" : "mp3";
    if (category === "3d")
        return detect3dExtension(url);
    return url.includes(".png") ? "png" : "webp";
}
async function runGeneration(category, opts) {
    const { config, flags, apiKey } = opts;
    const model = resolveDefaultModel(category, config, flags);
    if (!model) {
        throw new SACError(`Model is required.`, ExitCode.USAGE, getModelRequirementHint(category));
    }
    const providerDef = getProvider(model);
    if (!providerDef) {
        throw new SACError(`Unknown model "${model}". Run \`seawork sac models --category ${category}\` to see all options.`, ExitCode.USAGE);
    }
    if (providerDef.category !== category) {
        throw new SACError(`Model "${model}" is a ${providerDef.category} model. Use \`seawork sac ${providerDef.category}\` instead.`, ExitCode.USAGE);
    }
    const requiresPrompt = providerDef.requiresPrompt ? providerDef.requiresPrompt(model) : true;
    const prompt = typeof flags.prompt === "string" ? flags.prompt : undefined;
    if (!prompt && requiresPrompt) {
        throw new SACError("Prompt is required.", ExitCode.USAGE, getPromptRequirementHint(category));
    }
    const body = providerDef.buildBody(model, prompt ?? "", flags);
    if (config.dryRun) {
        return { request: body };
    }
    const url = generationEndpoint(config);
    if (!config.quiet) {
        process.stderr.write(`[${providerDef.provider}/${model}]\n`);
    }
    const createResp = await requestJson(config, {
        url,
        method: "POST",
        body,
    }, apiKey);
    const taskId = createResp.id;
    if (!taskId) {
        throw new SACError("No task ID returned from API.", ExitCode.GENERAL);
    }
    if (flags.async || config.async) {
        return { taskId, status: createResp.status };
    }
    const result = await pollTask(config, { taskId, apiKey });
    const urls = extractUrls(result);
    if (urls.length === 0) {
        return { taskId, status: result.status, raw: result };
    }
    if (typeof flags.outDir === "string" && flags.outDir.length > 0) {
        const outDir = flags.outDir;
        if (!existsSync(outDir))
            mkdirSync(outDir, { recursive: true });
        const prefix = typeof flags.outPrefix === "string" && flags.outPrefix.length > 0
            ? flags.outPrefix
            : getDefaultPrefix(category);
        const saved = [];
        for (let i = 0; i < urls.length; i++) {
            const ext = detectExtension(category, urls[i]);
            const filename = `${prefix}_${String(i + 1).padStart(3, "0")}.${ext}`;
            const destPath = join(outDir, filename);
            await downloadFile(urls[i], destPath, { quiet: config.quiet });
            saved.push(destPath);
        }
        return { taskId, saved };
    }
    return { taskId, urls };
}
export async function generateImage(opts) {
    return runGeneration("image", opts);
}
export async function generateVideo(opts) {
    return runGeneration("video", opts);
}
export async function generateAudio(opts) {
    return runGeneration("audio", opts);
}
export async function generate3d(opts) {
    return runGeneration("3d", opts);
}
/**
 * Query task status
 */
export async function queryTask(config, taskId, opts) {
    if (opts?.wait) {
        return pollTask(config, {
            taskId,
            intervalSec: opts.intervalSec,
            timeoutSec: opts.timeoutSec,
            apiKey: opts.apiKey,
        });
    }
    const { taskEndpoint } = await import("./endpoints.js");
    return requestJson(config, {
        url: taskEndpoint(config, taskId),
        method: "GET",
    }, opts?.apiKey);
}
/**
 * Extract result URLs from task response
 */
export function extractUrls(data) {
    const urls = [];
    for (const out of data.output ?? []) {
        for (const content of out.content ?? []) {
            if (content.url)
                urls.push(content.url);
        }
    }
    return urls;
}
//# sourceMappingURL=generate.js.map