"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.asEditorInterface = exports.generateEditorInterface = void 0;
const v3_1 = require("zod/v3");
const logger_1 = require("../utils/logger");
const GatewayClient_1 = require("./GatewayClient");
const log = (0, logger_1.createLogger)('InterfaceGenerationService');
const DEFAULT_GENERATION_MODEL = 'claude-sonnet-4-6';
const MAX_TOKENS = 4096;
const TEMPERATURE = 0.4;
/**
 * Generate an editor interface from a natural-language description — the
 * centerpiece of Generative Editor Interfaces (see
 * docs/generative-editor-interfaces.md). The model authors the affordances that
 * fit the task; we validate against a constrained starter vocabulary and wrap
 * each into a GeneratedControl. The renderer is open/forward-compatible, so the
 * vocabulary can grow without breaking older specs.
 */
const cssDeclSchema = v3_1.z.object({
    selector: v3_1.z.string().min(1),
    property: v3_1.z.string().min(1),
    value: v3_1.z.string().min(1),
});
const scopedSlider = v3_1.z.object({
    kind: v3_1.z.literal('scoped-slider'),
    label: v3_1.z.string().min(1),
    selector: v3_1.z.string().min(1),
    property: v3_1.z.string().min(1),
    min: v3_1.z.number(),
    max: v3_1.z.number(),
    step: v3_1.z.number().optional(),
    unit: v3_1.z.string().optional(),
    default: v3_1.z.union([v3_1.z.number(), v3_1.z.string()]).optional(),
});
const swatchSet = v3_1.z.object({
    kind: v3_1.z.literal('swatch-set'),
    label: v3_1.z.string().min(1),
    selector: v3_1.z.string().min(1),
    property: v3_1.z.string().min(1),
    swatches: v3_1.z.array(v3_1.z.string()).min(1),
    default: v3_1.z.string().optional(),
});
const optionCards = v3_1.z.object({
    kind: v3_1.z.literal('option-cards'),
    label: v3_1.z.string().min(1),
    options: v3_1.z
        .array(v3_1.z.object({
        id: v3_1.z.string().min(1),
        label: v3_1.z.string().min(1),
        declarations: v3_1.z.array(cssDeclSchema).min(1),
    }))
        .min(2),
    default: v3_1.z.string().optional(),
});
const controlSchema = v3_1.z.discriminatedUnion('kind', [
    scopedSlider,
    swatchSet,
    optionCards,
]);
const sectionSchema = v3_1.z.object({
    title: v3_1.z.string().min(1),
    controls: v3_1.z.array(controlSchema).min(1).max(10),
});
const responseSchema = v3_1.z.object({
    sections: v3_1.z.array(sectionSchema).min(1).max(6),
});
const GENERATE_TOOL = {
    name: 'submit_interface',
    strict: false,
    description: 'Submit the generated editor interface (sections of controls) for the described editing task. Call exactly once.',
    input_schema: {
        type: 'object',
        properties: {
            sections: {
                type: 'array',
                description: 'Ordered sections, each grouping related controls.',
                items: {
                    type: 'object',
                    properties: {
                        title: { type: 'string' },
                        controls: {
                            type: 'array',
                            description: 'Controls. Each has a "kind": "scoped-slider" (numeric CSS property on a selector), "swatch-set" (pick a color from a palette applied to a selector+property), or "option-cards" (choose between named directions, each a set of CSS declarations).',
                            items: {
                                type: 'object',
                                properties: {
                                    kind: {
                                        type: 'string',
                                        enum: ['scoped-slider', 'swatch-set', 'option-cards'],
                                    },
                                    label: { type: 'string' },
                                    // scoped-slider / swatch-set
                                    selector: {
                                        type: 'string',
                                        description: 'CSS selector targeting elements in the live preview (e.g. ".card", "button", "h1:hover").',
                                    },
                                    property: {
                                        type: 'string',
                                        description: 'kebab-case CSS property.',
                                    },
                                    min: { type: 'number' },
                                    max: { type: 'number' },
                                    step: { type: 'number' },
                                    unit: {
                                        type: 'string',
                                        description: 'Unit for slider values, e.g. "px", "ms", "em".',
                                    },
                                    swatches: {
                                        type: 'array',
                                        items: { type: 'string' },
                                        description: 'Palette of CSS colors for a swatch-set.',
                                    },
                                    // option-cards
                                    options: {
                                        type: 'array',
                                        description: 'Named directions for option-cards; each applies its CSS declarations when chosen.',
                                        items: {
                                            type: 'object',
                                            properties: {
                                                id: { type: 'string' },
                                                label: { type: 'string' },
                                                declarations: {
                                                    type: 'array',
                                                    items: {
                                                        type: 'object',
                                                        properties: {
                                                            selector: { type: 'string' },
                                                            property: { type: 'string' },
                                                            value: { type: 'string' },
                                                        },
                                                        required: ['selector', 'property', 'value'],
                                                    },
                                                },
                                            },
                                            required: ['id', 'label', 'declarations'],
                                        },
                                    },
                                    default: {
                                        description: 'Starting value (number/string for slider, color for swatch-set, option id for option-cards).',
                                    },
                                },
                                required: ['kind', 'label'],
                            },
                        },
                    },
                    required: ['title', 'controls'],
                },
            },
        },
        required: ['sections'],
    },
};
const SYSTEM_PROMPT = `You generate editing interfaces for Rivet — a visual editor over a live web prototype.

Given a natural-language description of an editing task, design the controls that best fit it. You are NOT limited to sliders; choose the right affordance:
- "scoped-slider": one numeric CSS property (selector + property + min/max/step/unit). Good for size, spacing, radius, duration, opacity.
- "swatch-set": pick a color from a curated palette, applied to a selector + property. Good for theming.
- "option-cards": choose between 2-4 named directions, each a small set of CSS declarations. Good for "make it feel X" choices where several properties move together.

Rules:
- Pick STABLE, SPECIFIC selectors a real page would have (classes, tags, ":hover" for interaction).
- Keep each section focused (1-5 controls). Generate only what the task needs.
- Provide sensible defaults and ranges. For option-cards, make the directions genuinely distinct.`;
const slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 32);
const buildPrompt = (params) => {
    const parts = [`Editing task: ${params.description.trim()}`];
    if (params.existingSectionTitles?.length) {
        parts.push(`The board already has these sections (avoid duplicating): ${params.existingSectionTitles.join(', ')}.`);
    }
    if (params.elementSummary) {
        parts.push(`Currently selected element:\n${params.elementSummary}`);
    }
    if (params.tokenSummary) {
        parts.push(`Project design tokens:\n${params.tokenSummary}`);
    }
    return parts.join('\n\n');
};
const toControl = (input, sectionIndex, controlIndex) => {
    const id = `ctl-${sectionIndex}-${controlIndex}-${slug(input.label)}`;
    const base = {
        id,
        kind: input.kind,
        label: input.label,
        default: input.default,
        baseline: input.default,
    };
    if (input.kind === 'scoped-slider') {
        return {
            ...base,
            binding: {
                selector: input.selector,
                property: input.property,
                status: 'resolved',
            },
            config: {
                min: input.min,
                max: input.max,
                step: input.step,
                unit: input.unit,
            },
        };
    }
    if (input.kind === 'swatch-set') {
        return {
            ...base,
            binding: {
                selector: input.selector,
                property: input.property,
                status: 'resolved',
            },
            config: { swatches: input.swatches },
        };
    }
    // option-cards
    return {
        ...base,
        default: input.default ?? input.options[0]?.id,
        baseline: input.default ?? input.options[0]?.id,
        config: { options: input.options },
    };
};
const generateEditorInterface = async (params) => {
    const description = params.description.trim();
    if (!description)
        throw new Error('description is required');
    const gateway = new GatewayClient_1.GatewayClient();
    const startedAt = Date.now();
    log.info(`Interface generation — model=${params.model ?? DEFAULT_GENERATION_MODEL}`);
    const toolInput = await gateway.sendToolUse({
        model: params.model ?? DEFAULT_GENERATION_MODEL,
        maxTokens: MAX_TOKENS,
        temperature: TEMPERATURE,
        system: SYSTEM_PROMPT,
        prompt: buildPrompt(params),
        tool: GENERATE_TOOL,
        cacheSystemPrompt: true,
    });
    const parsed = responseSchema.safeParse(toolInput);
    if (!parsed.success) {
        log.warn('Interface generation failed schema validation', {
            validationError: parsed.error.flatten(),
        });
        throw new Error('Invalid interface generation response from model');
    }
    log.info(`Interface generation completed in ${Date.now() - startedAt}ms`);
    return parsed.data.sections.map((section, si) => ({
        id: `sec-${si}-${slug(section.title)}`,
        title: section.title,
        controls: section.controls.map((c, ci) => toControl(c, si, ci)),
    }));
};
exports.generateEditorInterface = generateEditorInterface;
/** Convenience: wrap sections into a full EditorInterface. */
const asEditorInterface = (intent, sections) => ({ version: 1, intent, sections });
exports.asEditorInterface = asEditorInterface;
//# sourceMappingURL=InterfaceGenerationService.js.map