import fsp from 'node:fs/promises';
import path from 'node:path';
import { sha256 } from './fsx.js';
import { redactCodexOutput, structuredOutputBlocker, validateStructuredOutput } from './codex-exec-output-schema.js';
export function strictJsonSchemaFormat(schemaName, jsonSchema) {
    const normalized = ensureStrictObjectSchema(jsonSchema);
    return {
        type: 'json_schema',
        name: schemaName.replace(/[^A-Za-z0-9_-]+/g, '_').slice(0, 64) || 'sks_schema',
        strict: true,
        schema: normalized
    };
}
export function ensureStrictObjectSchema(schema) {
    const next = { ...schema };
    if (next.type !== 'object')
        next.type = 'object';
    const properties = next.properties && typeof next.properties === 'object'
        ? next.properties
        : {};
    next.properties = Object.fromEntries(Object.entries(properties).map(([key, value]) => [
        key,
        value && typeof value === 'object' && !Array.isArray(value)
            ? ensureNestedStrictSchema(value)
            : value
    ]));
    next.required = Array.isArray(next.required) ? next.required : Object.keys(properties);
    next.additionalProperties = false;
    return next;
}
export async function runOpenAIStructuredOutput(request) {
    const model = request.model || process.env.OPENAI_STRUCTURED_OUTPUT_MODEL || 'gpt-5.4-mini';
    const apiKey = request.apiKey || process.env.OPENAI_API_KEY || null;
    const sourceSha = request.imagePath ? sha256(await fsp.readFile(path.resolve(request.imagePath))) : null;
    if (!apiKey) {
        return {
            schema: 'sks.structured-output-adapter-result.v1',
            ok: false,
            status: 'integration_optional',
            provider: 'openai_responses_text_format',
            model,
            parsed_json: null,
            validation: { ok: false, issues: ['openai_api_key_missing'] },
            blocker: structuredOutputBlocker('openai_api_key_missing', 'Set OPENAI_API_KEY to enable Structured Outputs fallback.'),
            setup_guidance: 'Set OPENAI_API_KEY or use Codex 0.133 codex exec resume --output-schema; do not accept unstructured extraction as verified evidence.',
            source_sha256: sourceSha
        };
    }
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), request.timeoutMs || 120_000);
    try {
        const response = await fetch('https://api.openai.com/v1/responses', {
            method: 'POST',
            headers: {
                authorization: `Bearer ${apiKey}`,
                'content-type': 'application/json'
            },
            body: JSON.stringify({
                model,
                input: [
                    {
                        role: 'user',
                        content: await structuredInputContent(request)
                    }
                ],
                text: { format: strictJsonSchemaFormat(request.schemaName, request.jsonSchema) }
            }),
            signal: controller.signal
        });
        const text = await response.text();
        if (!response.ok) {
            return blockedResult(model, 'openai_structured_output_api_error', redactCodexOutput(text), sourceSha);
        }
        const payload = JSON.parse(text);
        const parsed = payload.output_parsed || parseResponseOutputText(payload);
        if (!parsed || typeof parsed !== 'object')
            return blockedResult(model, 'json_parse_failed', 'Responses output did not contain parsed JSON.', sourceSha);
        const strictSchema = strictJsonSchemaFormat(request.schemaName, request.jsonSchema).schema;
        const validation = validateStructuredOutput(parsed, strictSchema);
        return {
            schema: 'sks.structured-output-adapter-result.v1',
            ok: validation.ok,
            status: validation.ok ? 'parsed' : 'blocked',
            provider: 'openai_responses_text_format',
            model,
            parsed_json: validation.ok ? parsed : null,
            validation,
            blocker: validation.ok ? null : structuredOutputBlocker('schema_validation_failed', validation.issues.join(', ')),
            setup_guidance: null,
            source_sha256: sourceSha
        };
    }
    catch (err) {
        return blockedResult(model, 'openai_structured_output_api_error', err instanceof Error ? err.message : String(err), sourceSha);
    }
    finally {
        clearTimeout(timer);
    }
}
async function structuredInputContent(request) {
    const parts = [{ type: 'input_text', text: request.prompt }];
    if (request.imagePath) {
        const absolute = path.resolve(request.imagePath);
        const data = await fsp.readFile(absolute);
        const mime = mimeForPath(absolute);
        parts.push({ type: 'input_image', image_url: `data:${mime};base64,${data.toString('base64')}` });
    }
    return parts;
}
function ensureNestedStrictSchema(schema) {
    if (schema.type === 'object' || schema.properties)
        return ensureStrictObjectSchema(schema);
    if (schema.type === 'array' && schema.items && typeof schema.items === 'object' && !Array.isArray(schema.items)) {
        return { ...schema, items: ensureNestedStrictSchema(schema.items) };
    }
    return schema;
}
function parseResponseOutputText(payload) {
    const chunks = Array.isArray(payload.output) ? payload.output : [];
    for (const item of chunks) {
        const content = Array.isArray(item?.content) ? item.content : [];
        for (const part of content) {
            if (part?.parsed)
                return part.parsed;
            if (typeof part?.text === 'string') {
                try {
                    return JSON.parse(part.text);
                }
                catch { }
            }
        }
    }
    return null;
}
function blockedResult(model, reason, detail, sourceSha) {
    return {
        schema: 'sks.structured-output-adapter-result.v1',
        ok: false,
        status: 'blocked',
        provider: 'openai_responses_text_format',
        model,
        parsed_json: null,
        validation: { ok: false, issues: [reason] },
        blocker: structuredOutputBlocker(reason, redactCodexOutput(detail)),
        setup_guidance: null,
        source_sha256: sourceSha
    };
}
function mimeForPath(file) {
    const ext = path.extname(file).toLowerCase();
    if (ext === '.jpg' || ext === '.jpeg')
        return 'image/jpeg';
    if (ext === '.webp')
        return 'image/webp';
    return 'image/png';
}
//# sourceMappingURL=structured-output-adapter.js.map