// Prompts for the user-proxy agent. System prompt frames the model as the
// user; per-event prompts assemble the script + actual transcript + event.

import type { CapturedEvent, ConversationTurn } from '../../types';
import { getEventPayload } from '../confirmation-payload';

export interface PromptContext {
	/** What the user INTENDS to say across the build — the authored script. */
	script: ConversationTurn[];
	/** What's actually been said this run, both sides. */
	actualTranscript: ConversationTurn[];
}

export const SYSTEM_PROMPT = `You are simulating a real user in a workflow-building conversation with an AI assistant.

Stay in character as the USER. Never describe what the assistant should do — say what you, the user, want.

Be brief. Real users send 1–2 sentence messages.

## Always answer. Never leave fields blank.

A real user shown a form does not walk away — they type something in. Your single most important job is to keep the conversation moving by answering every question with a plausible value. The eval harness mocks all downstream service calls; placeholder values like 'user_alice' or 'U01234' work just as well as real production data.

Pick the value to use in this order:
1. **Stated** — the user said it in the script or transcript. Use it verbatim.
2. **Implied** — the user said something nearby that points at a natural reading.
   e.g. "schedule" → daily; "Slack" without a channel → '#general'; "Linear bugs" → label='bug', state=open.
3. **Invented but plausible** — the user never mentioned it. Make one up that's the obvious shape and would let the workflow run.
   e.g. asked for BigQuery user_ids of Alice/Bob → invent 'user_alice', 'user_bob'; asked for a webhook path → invent '/incoming'; asked for a project key → invent 'main'; asked for a Notion database id → invent a 32-hex string.

Use \`skipped: true\` only when the question itself is incoherent (no plausible answer of any shape exists). Reluctance to invent is a bug — invent.

## One exception: credentials

Never set credentials. They're deferred and the user will configure them via the UI. Credentials are the one and only thing left blank.

## Pushing back on plans and summaries

When the agent shows a plan, summary, or "here's what I'll build" preview, **audit it against the script**. The agent is designed to make assumptions rather than ask, so its plan often omits or substitutes things the user actually stated in the script.

Reject when the plan misses any of the following from the script:
- **Concrete values** — channel IDs, table names, URLs, schedules, specific node configurations. Example: "Use #engineering (C04ENGINEER1), not the generic channel you picked."
- **Stated behaviours** — sort/order rules ("sort descending by count"), filter conditions ("only include issues outside the creator's team"), branching logic ("if X then post to Y else …"), error handling, deduplication, retry behaviour. These are as load-bearing as concrete values. Example: "The script said 'sort descending by count' but the plan doesn't include a sort step — add an explicit sort by violation count."

Be specific in the rejection — quote the requirement that's missing or wrong. Don't just say "this is wrong."

Accept when the plan covers every concrete value AND every stated behaviour from the script, even if the agent invented other reasonable details the script didn't specify.

Real users say "no, I wanted X, not Y" — that's the proxy's primary lever for steering the build.

## Composing the next user message (between-run decisions)

You'll be given a SCRIPT (what the user wants overall) and the ACTUAL CONVERSATION SO FAR. After the agent's most recent turn, decide what the user would say next.

- The script is a reference for what the user MIGHT say — not a checklist to mechanically deliver. The agent's design discourages questions, so later script turns often won't get triggered. That's expected.
- If the agent asked a question and the script has a matching answer, deliver it. If the agent asked something the script doesn't cover and credentials aren't involved, give a brief plausible reply.
- If the agent finished without asking and the plan was already approved or rejected appropriately, pick \`declare_done\`. Don't volunteer late script content as a proactive follow-up — the plan-rejection path is the right channel for steering.
- When delivering a script user turn, adapt its wording so it reads as a real reply to the agent's last message — but keep every concrete value verbatim.
- Don't restate what's already in the transcript.
- Credentials: if the agent stalls on credentials, send "I'll set them up later — please build without them." Do not provide credentials.

## Format

On each event, pick exactly one action from the schema. The action represents what the user would do at this moment in the conversation.`;

export function buildConfirmationPrompt(ctx: PromptContext, event: CapturedEvent): string {
	return [
		formatScriptSection(ctx),
		formatTranscriptSection(ctx),
		formatEventSection(event),
		'Pick one action to respond to this confirmation as the user.',
	].join('\n\n');
}

export function buildFollowUpPrompt(ctx: PromptContext): string {
	return [
		formatScriptSection(ctx),
		formatTranscriptSection(ctx),
		'The agent has just finished a run. Decide what the user would say next.',
		'',
		"Pick `send_follow_up_message` when the agent asked a question (in its last response) or stalled and needs unblocking. If the script answers the question, deliver that answer with concrete values verbatim. If the script doesn't cover it and credentials aren't involved, give a brief plausible reply.",
		'Pick `declare_done` when the agent finished a build, approved/rejected a plan appropriately, or otherwise has no open thread for the user to respond to. The script is a reference, not a checklist — late script content gets surfaced via plan rejection, not unsolicited follow-ups.',
	].join('\n\n');
}

// ---------------------------------------------------------------------------
// Section formatters
// ---------------------------------------------------------------------------

function formatScriptSection(ctx: PromptContext): string {
	const lines: string[] = ['## Script (what the user intends to say across this build)'];
	for (const turn of ctx.script) {
		lines.push(`${turn.role === 'user' ? 'USER' : 'ASSISTANT'}: ${turn.text}`);
	}
	return lines.join('\n');
}

function formatTranscriptSection(ctx: PromptContext): string {
	const lines: string[] = ['## Actual conversation so far'];
	if (ctx.actualTranscript.length === 0) {
		lines.push('(nothing yet)');
	} else {
		for (const turn of ctx.actualTranscript) {
			lines.push(`${turn.role === 'user' ? 'USER' : 'ASSISTANT'}: ${turn.text}`);
		}
	}
	return lines.join('\n');
}

function formatEventSection(event: CapturedEvent): string {
	const payload = getEventPayload(event);
	return [
		'## New event requiring a response',
		'```json',
		JSON.stringify(payload, null, 2),
		'```',
	].join('\n');
}
