"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerVoiceRoutes = registerVoiceRoutes;
const zod_1 = require("zod");
const index_js_1 = require("../../lib/db/index.js");
const api_response_js_1 = require("../api-response.js");
const Lineage = zod_1.z.enum(['anthropic', 'openai', 'google', 'opencode', 'moonshot']);
const Source = zod_1.z.enum(['cli', 'api']);
const ListQuerySchema = zod_1.z.object({
    lineage: Lineage.optional(),
    source: Source.optional(),
    provider: zod_1.z.string().optional(),
    enabled: zod_1.z
        .enum(['true', 'false'])
        .optional()
        .transform((v) => (v === undefined ? undefined : v === 'true')),
});
// Cost fields: $/Mtok must be a finite, non-negative number. `null` is a
// distinct legitimate state ("unknown / not set"), `0` is also legitimate
// (free tier, opencode-go subscription, plan-priced reviewers). Negative
// or non-finite (NaN, Infinity) costs would corrupt downstream cost
// dashboards and the run-page chips, so we reject them at the boundary
// rather than letting bad values reach the DB.
const Cost = zod_1.z.number().finite().min(0).nullable().optional();
const PostBodySchema = zod_1.z.object({
    provider: zod_1.z.string().min(1),
    model_id: zod_1.z.string().min(1),
    label: zod_1.z.string().min(1),
    source: Source.default('api'),
    lineage: Lineage,
    vendor_family: zod_1.z.string().nullable().optional(),
    input_cost_per_mtok: Cost,
    output_cost_per_mtok: Cost,
    enabled: zod_1.z.boolean().optional(),
});
const PutBodySchema = zod_1.z.object({
    label: zod_1.z.string().min(1).optional(),
    enabled: zod_1.z.boolean().optional(),
    input_cost_per_mtok: Cost,
    output_cost_per_mtok: Cost,
});
function registerVoiceRoutes(fastify) {
    // List voices, optionally filtered. Default = all rows (incl. disabled).
    fastify.get('/voices', async (request, reply) => {
        try {
            const parsed = ListQuerySchema.safeParse(request.query);
            if (!parsed.success) {
                return (0, api_response_js_1.sendError)(reply, 'validation', parsed.error.message);
            }
            const items = await index_js_1.voices.list(parsed.data);
            return (0, api_response_js_1.successResponse)((0, api_response_js_1.listEnvelope)(items));
        }
        catch (error) {
            const message = error instanceof Error ? error.message : 'Unknown error';
            return (0, api_response_js_1.errorResponse)('db_error', message);
        }
    });
    // Single-row read.
    fastify.get('/voices/:id', async (request, reply) => {
        try {
            const v = await index_js_1.voices.getById(request.params.id);
            if (!v) {
                return (0, api_response_js_1.sendError)(reply, 'not_found', `Voice ${request.params.id} not found`);
            }
            return (0, api_response_js_1.successResponse)(v);
        }
        catch (error) {
            const message = error instanceof Error ? error.message : 'Unknown error';
            return (0, api_response_js_1.errorResponse)('db_error', message);
        }
    });
    // Add a voice (used by OpenRouter inline + by direct API additions).
    fastify.post('/voices', async (request, reply) => {
        try {
            const parsed = PostBodySchema.safeParse(request.body);
            if (!parsed.success) {
                return (0, api_response_js_1.sendError)(reply, 'validation', parsed.error.message);
            }
            const id = `${parsed.data.provider}:${parsed.data.model_id}`;
            const existing = await index_js_1.voices.getById(id);
            if (existing) {
                return (0, api_response_js_1.sendError)(reply, 'conflict', `Voice ${id} already exists`);
            }
            const row = await index_js_1.voices.upsert({
                id,
                label: parsed.data.label,
                source: parsed.data.source,
                provider: parsed.data.provider,
                model_id: parsed.data.model_id,
                lineage: parsed.data.lineage,
                vendor_family: parsed.data.vendor_family ?? null,
                input_cost_per_mtok: parsed.data.input_cost_per_mtok ?? null,
                output_cost_per_mtok: parsed.data.output_cost_per_mtok ?? null,
                enabled: parsed.data.enabled,
            });
            return (0, api_response_js_1.successResponse)(row);
        }
        catch (error) {
            const message = error instanceof Error ? error.message : 'Unknown error';
            return (0, api_response_js_1.errorResponse)('db_error', message);
        }
    });
    // Toggle enabled / update label / update costs.
    // source/provider/lineage/vendor_family/model_id are immutable post-create.
    // Note: model_id IS rewritten on cli rows during the seed loop, but only
    // by the seed (via voices.upsert), not via this route.
    fastify.put('/voices/:id', async (request, reply) => {
        try {
            const parsed = PutBodySchema.safeParse(request.body);
            if (!parsed.success) {
                return (0, api_response_js_1.sendError)(reply, 'validation', parsed.error.message);
            }
            const existing = await index_js_1.voices.getById(request.params.id);
            if (!existing) {
                return (0, api_response_js_1.sendError)(reply, 'not_found', `Voice ${request.params.id} not found`);
            }
            const row = await index_js_1.voices.update(request.params.id, parsed.data);
            return (0, api_response_js_1.successResponse)(row);
        }
        catch (error) {
            const message = error instanceof Error ? error.message : 'Unknown error';
            return (0, api_response_js_1.errorResponse)('db_error', message);
        }
    });
    // DELETE allowed for both cli + api source. cli rows auto-heal on next
    // seed if the model is still detected by the gateway (per gem-2 round 1
    // MED — unblocks cleanup of deprecated OpenCode models).
    fastify.delete('/voices/:id', async (request) => {
        try {
            await index_js_1.voices.delete(request.params.id);
            return (0, api_response_js_1.successResponse)({ id: request.params.id, deleted: true });
        }
        catch (error) {
            const message = error instanceof Error ? error.message : 'Unknown error';
            return (0, api_response_js_1.errorResponse)('db_error', message);
        }
    });
}
//# sourceMappingURL=voices.js.map