/**
 * browser/driver.mjs — bundled browser driver.
 *
 * Maps IDL verbs to one of two transports: the Puppeteer MCP server or
 * the `agent-browser` skill. The manifest declares both:
 *   transport.mcp_server_id = "puppeteer"   (legacy / non-Skill hosts)
 *   transport.skill_id      = "agent-browser" (Claude Code / OpenCode)
 *
 * Transport selection lives in the WORKFLOW RUNNER layer — that's the
 * site that knows whether the host CLI exposes the Skill tool. This
 * driver exposes `chooseTransport({has_skill_tool})` as a helper so the
 * workflow runner can ask the driver which transport it would pick;
 * the driver records the chosen transport on each VerbResult via the
 * optional `transport: 'skill' | 'mcp'` field (additive — existing
 * consumers that ignore the field keep working).
 *
 * Per ID-01 — the workflow prose NEVER names mcp__puppeteer__* tools
 * OR concrete `agent-browser` CLI subcommands. Both mappings live ONLY
 * here in driver.mjs.
 *
 * IDL verb → MCP tool mapping (legacy transport):
 *   navigate       → mcp__puppeteer__puppeteer_navigate
 *   click          → mcp__puppeteer__puppeteer_click
 *   type           → mcp__puppeteer__puppeteer_fill
 *   screenshot     → mcp__puppeteer__puppeteer_screenshot
 *   assert_visible → mcp__puppeteer__puppeteer_evaluate (DOM selector)
 *   assert_text    → mcp__puppeteer__puppeteer_evaluate (textContent match)
 *   wait_for_idle  → mcp__puppeteer__puppeteer_evaluate (networkidle check)
 *   execute_js     → mcp__puppeteer__puppeteer_evaluate
 *
 * IDL verb → agent-browser CLI mapping (Skill transport):
 *   navigate       → agent-browser open <url>
 *   click          → agent-browser click @e<N>
 *   type           → agent-browser fill @e<N> <text>
 *   screenshot     → agent-browser screenshot
 *   assert_visible → agent-browser snapshot + agent-browser get role <selector>
 *   assert_text    → agent-browser snapshot + agent-browser get text <selector>
 *   wait_for_idle  → agent-browser wait --load networkidle
 *   execute_js     → agent-browser eval <script>
 *
 * The driver is a thin adapter: it validates input shape via the IDL
 * arg-metadata, builds the transport-specific args, and returns a
 * VerbResult. The actual transport dispatch happens at the workflow
 * runner layer (which holds the model's tool surface — `Skill(...)` for
 * skill transport, `mcp__puppeteer__*` for MCP transport). For
 * conformance testing we expose the verb signatures as identity stubs
 * returning VerbResult — the SDK conformance kit then validates the
 * shape contract.
 */

import { validateVerbArgs } from '../../src/cli/verify-proof/idl/index.mjs'

/**
 * Pick a transport given the host environment's capabilities. When the
 * host exposes the Skill tool (Claude Code, OpenCode), prefer the
 * `agent-browser` skill — one fewer external dependency (no Puppeteer
 * MCP server install). Otherwise fall back to the MCP transport.
 *
 * @param {{has_skill_tool: boolean}} env
 * @returns {('skill'|'mcp')}
 */
export function chooseTransport(env) {
	if (env && env.has_skill_tool === true) return 'skill'
	return 'mcp'
}

/** Build a VerbResult given the actual transport-layer result. */
function ok(evidence_paths = [], transport) {
	const result = { status: 'success', evidence_paths, duration_ms: 0, retry_count: 0 }
	if (transport) result.transport = transport
	return result
}
function fail(error, transport) {
	const result = { status: 'fail', evidence_paths: [], duration_ms: 0, retry_count: 0, error }
	if (transport) result.transport = transport
	return result
}

/**
 * Validate args + return a VerbResult shape stub. Real transport
 * invocation happens at the workflow-runner layer (model holds the tool
 * surface — `Skill({skill: "agent-browser", args})` OR
 * `mcp__puppeteer__*` depending on host capabilities).
 *
 * @param {string} verb
 * @param {Record<string, unknown>} args
 * @param {('skill'|'mcp')} [transport] — optional, recorded on the result for audit
 * @returns {{status: 'success'|'fail'|'skip', evidence_paths: string[], duration_ms: number, retry_count: number, error?: string, transport?: 'skill'|'mcp'}}
 */
function adaptVerb(verb, args, transport) {
	const check = validateVerbArgs(verb, args)
	if (!check.valid) {
		return fail(`browser.${verb}: missing required args: ${check.missing.join(', ')}`, transport)
	}
	// Identity stub for the conformance test surface. Production execution
	// dispatches to Skill({skill: "agent-browser", ...}) OR mcp__puppeteer__*
	// at the workflow-runner layer.
	if (verb === 'screenshot' && typeof args.output_path === 'string') {
		return ok([args.output_path], transport)
	}
	return ok([], transport)
}

export async function navigate(args) {
	return adaptVerb('navigate', args)
}

export async function click(args) {
	return adaptVerb('click', args)
}

export async function type(args) {
	return adaptVerb('type', args)
}

export async function screenshot(args) {
	return adaptVerb('screenshot', args)
}

export async function assert_visible(args) {
	return adaptVerb('assert_visible', args)
}

export async function assert_text(args) {
	return adaptVerb('assert_text', args)
}

export async function wait_for_idle(args) {
	return adaptVerb('wait_for_idle', args)
}

export async function execute_js(args) {
	return adaptVerb('execute_js', args)
}
