"use strict";
/**
 * Commit the agent's instrumented event plan to the project's Amplitude
 * tracking plan as *planned* events.
 *
 * Runs after the agent finishes instrumenting (i.e. `.amplitude-events.json`
 * is populated and `track()` calls are written).
 *
 * Step 1 — POST `/v1/planned-events` on the wizard proxy (OAuth Bearer, same
 * host as `createAmplitudeApp`). If the route is not deployed yet (HTTP 404),
 * falls back to the Amplitude MCP `create_events` tool with `wasPlanned: true`.
 *
 * Step 2 — Amplitude MCP `update_event` to attach descriptions (no HTTP
 * equivalent yet).
 *
 * Errors are swallowed into a result object — a failure here should never
 * derail the happy-path outro.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.commitPlannedEvents = commitPlannedEvents;
const axios_1 = __importDefault(require("axios"));
const zod_1 = require("zod");
const debug_js_1 = require("../utils/debug.js");
const constants_js_1 = require("./constants.js");
const api_js_1 = require("./api.js");
const mcp_with_fallback_js_1 = require("./mcp-with-fallback.js");
const jwt_exp_js_1 = require("../utils/jwt-exp.js");
const urls_js_1 = require("../utils/urls.js");
const PlannedEventsSuccessSchema = zod_1.z.object({
    createdCount: zod_1.z.number(),
    eventTypes: zod_1.z.array(zod_1.z.string()),
    appId: zod_1.z.string(),
});
const PlannedEventsErrorSchema = zod_1.z.object({
    error: zod_1.z.object({
        code: zod_1.z.string(),
        message: zod_1.z.string(),
    }),
});
const CreateEventsResponse = zod_1.z.object({
    success: zod_1.z.boolean().optional(),
    createdEvents: zod_1.z.array(zod_1.z.string()).optional(),
    message: zod_1.z.string().optional(),
});
const UpdateEventResponse = zod_1.z.object({
    success: zod_1.z.boolean().optional(),
});
const CREATE_EVENTS_TIMEOUT_MS = 30_000;
const PLANNED_EVENTS_HTTP_TIMEOUT_MS = 60_000;
function dedupeAndClean(events) {
    const seen = new Set();
    const out = [];
    for (const e of events) {
        const name = e.name?.trim();
        if (!name)
            continue;
        if (seen.has(name))
            continue;
        seen.add(name);
        out.push({ name, description: (e.description ?? '').trim() });
    }
    return out;
}
async function createPlannedEventsViaHttp(accessToken, zone, appId, events, abortSignal) {
    const url = `${(0, api_js_1.getWizardProxyBase)(zone)}/v1/planned-events`;
    try {
        const response = await axios_1.default.post(url, { appId, events }, {
            headers: {
                Authorization: `Bearer ${accessToken}`,
                'Content-Type': 'application/json',
                'User-Agent': constants_js_1.WIZARD_USER_AGENT,
            },
            validateStatus: () => true,
            timeout: PLANNED_EVENTS_HTTP_TIMEOUT_MS,
            signal: abortSignal,
        });
        if (response.status === 404) {
            return {
                kind: 'error',
                message: 'planned-events endpoint not found',
                notFound: true,
            };
        }
        if (response.status >= 200 && response.status < 300) {
            const parsed = PlannedEventsSuccessSchema.safeParse(response.data);
            if (parsed.success) {
                return {
                    kind: 'ok',
                    createdEvents: parsed.data.eventTypes,
                };
            }
            (0, debug_js_1.logToFile)(`[commitPlannedEvents] planned-events success body parse failed: ${JSON.stringify(response.data)}`);
            return {
                kind: 'error',
                message: 'Invalid response from planned-events endpoint',
                notFound: false,
            };
        }
        const errParsed = PlannedEventsErrorSchema.safeParse(response.data);
        const msg = errParsed.success
            ? `${errParsed.data.error.code}: ${errParsed.data.error.message}`
            : `HTTP ${response.status}`;
        return { kind: 'error', message: msg, notFound: false };
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        (0, debug_js_1.logToFile)(`[commitPlannedEvents] planned-events HTTP error: ${message}`);
        return { kind: 'error', message, notFound: false };
    }
}
async function createPlannedEventsViaMcp(accessToken, appId, mcpEvents, mcpUrl, abortSignal) {
    return (0, mcp_with_fallback_js_1.callAmplitudeMcp)({
        accessToken,
        mcpUrl,
        label: 'commitPlannedEvents.create',
        abortSignal,
        agentTimeoutMs: CREATE_EVENTS_TIMEOUT_MS,
        direct: async (callTool) => {
            const text = await callTool(1, 'create_events', {
                projectId: appId,
                events: mcpEvents,
            });
            if (!text)
                return null;
            // Hard short-circuit: Amplitude MCP may return plain text starting with
            // "MCP error" when create_events is not implemented for the session.
            // Return a sentinel so callAmplitudeMcp does not burn ~20s on an agent
            // fallback that reaches the same outcome.
            if (text.startsWith('MCP error')) {
                (0, debug_js_1.logToFile)('[commitPlannedEvents] create_events not available on Amplitude MCP — events will register on first ingestion');
                return {
                    success: false,
                    createdEvents: [],
                    message: 'create_events tool not available on Amplitude MCP server',
                };
            }
            try {
                const parsed = CreateEventsResponse.parse(JSON.parse(text));
                return {
                    success: parsed.success ?? false,
                    createdEvents: parsed.createdEvents ?? [],
                    message: parsed.message,
                };
            }
            catch (e) {
                (0, debug_js_1.logToFile)(`[commitPlannedEvents] create_events parse error: ${e instanceof Error ? e.message : String(e)}`);
                return null;
            }
        },
        agentPrompt: `Use the Amplitude MCP tool create_events to add these events to project ${appId} as planned events.
Pass wasPlanned: true for every event.
Events: ${JSON.stringify(mcpEvents)}
Respond with JSON only — no prose, no markdown fences:
{"success":true|false,"createdEvents":["..."],"message":"..."}`,
        parseAgent: (text) => {
            const match = text.match(/\{[\s\S]*\}/);
            if (!match)
                return null;
            try {
                const parsed = CreateEventsResponse.parse(JSON.parse(match[0]));
                return {
                    success: parsed.success ?? false,
                    createdEvents: parsed.createdEvents ?? [],
                    message: parsed.message,
                };
            }
            catch {
                return null;
            }
        },
    });
}
/**
 * Commit the event plan to the Amplitude tracking plan as planned events.
 *
 * Returns counts instead of throwing — failure here must not break the outro.
 */
async function commitPlannedEvents(opts) {
    const { accessToken, appId, events, zone, abortSignal } = opts;
    const accountZone = (0, jwt_exp_js_1.decodeJwtZone)(accessToken) ?? zone;
    const mcpUrl = (0, urls_js_1.getMcpUrlFromZone)(accountZone);
    const cleaned = dedupeAndClean(events);
    if (cleaned.length === 0 || !appId) {
        return { attempted: 0, created: 0, described: 0 };
    }
    const mcpEvents = cleaned.map((e) => ({
        eventType: e.name,
        wasPlanned: true,
    }));
    // ── Step 1: wizard-proxy POST /v1/planned-events (MCP fallback on 404) ───
    const httpResult = await createPlannedEventsViaHttp(accessToken, zone, appId, mcpEvents, abortSignal);
    let createResult;
    if (httpResult.kind === 'ok') {
        createResult = {
            success: true,
            createdEvents: httpResult.createdEvents,
        };
    }
    else if (httpResult.notFound) {
        (0, debug_js_1.logToFile)('[commitPlannedEvents] planned-events returned 404 — falling back to MCP create_events');
        createResult = (await createPlannedEventsViaMcp(accessToken, appId, mcpEvents, mcpUrl, abortSignal)) ?? { success: false, message: 'create_events failed' };
    }
    else {
        createResult = {
            success: false,
            message: httpResult.message,
        };
    }
    if (!createResult || createResult.success !== true) {
        const errMsg = createResult?.message ?? 'planned events create failed';
        (0, debug_js_1.logToFile)(`[commitPlannedEvents] ${errMsg}`);
        return {
            attempted: cleaned.length,
            created: 0,
            described: 0,
            error: errMsg,
        };
    }
    const created = createResult.createdEvents ?? cleaned.map((e) => e.name);
    // ── Step 2: update_event with descriptions ────────────────────────────────
    const descriptions = {};
    for (const e of cleaned) {
        if (e.description && created.includes(e.name)) {
            descriptions[e.name] = e.description;
        }
    }
    if (Object.keys(descriptions).length === 0) {
        return { attempted: cleaned.length, created: created.length, described: 0 };
    }
    const updateResult = await (0, mcp_with_fallback_js_1.callAmplitudeMcp)({
        accessToken,
        mcpUrl,
        label: 'commitPlannedEvents.update',
        abortSignal,
        agentTimeoutMs: CREATE_EVENTS_TIMEOUT_MS,
        direct: async (callTool) => {
            const text = await callTool(2, 'update_event', {
                projectId: appId,
                descriptions,
            });
            if (!text)
                return null;
            try {
                const parsed = UpdateEventResponse.parse(JSON.parse(text));
                return { success: parsed.success ?? false };
            }
            catch {
                return null;
            }
        },
        agentPrompt: `Use the Amplitude MCP tool update_event to set descriptions on project ${appId}.
Descriptions map: ${JSON.stringify(descriptions)}
Respond with JSON only: {"success":true|false}`,
        parseAgent: (text) => {
            const match = text.match(/\{[\s\S]*\}/);
            if (!match)
                return null;
            try {
                const parsed = UpdateEventResponse.parse(JSON.parse(match[0]));
                return { success: parsed.success ?? false };
            }
            catch {
                return null;
            }
        },
    });
    const described = updateResult?.success === true ? Object.keys(descriptions).length : 0;
    return {
        attempted: cleaned.length,
        created: created.length,
        described,
    };
}
