/**
 * Take screenshot tool -- captures spreadsheet ranges as images and analyzes
 * them with a vision LLM (Gemini Flash) via the Shortcut LLM proxy.
 *
 * Flow:
 * 1. AgentController calls take_screenshot with one or more sheet:range pairs and a query
 * 2. Tool requests screenshots from the client via a pluggable operations interface
 * 3. Screenshots come back as base64 PNGs
 * 4. Tool calls the vision LLM via the proxy with the images + query
 * 5. Returns text analysis to the agent (no raw images in context)
 *
 * The operations interface (TakeScreenshotOperations) is pluggable:
 * - TUI mode: captures via @mog-sdk/node wb.captureScreenshot() (headless render)
 * - Server mode: delegates to the connected client via ScreenshotBroker
 * - Fallback: returns empty (no screenshots) when neither is available
 */
import { Type } from '@sinclair/typebox';
import { TAKE_SCREENSHOT } from '../../tool-names.js';
import { mapShortcutLlmProxyError } from '../providers/shortcut-llm-proxy-client.js';
import { readShortcutSSE } from '../providers/shortcut-sse.js';
const TOOL_NAME = TAKE_SCREENSHOT;
const DEFAULT_VISION_MODEL = 'gemini-3-flash-preview';
const SCREENSHOT_LLM_MAX_TOKENS = 16384;
const SCREENSHOT_LLM_TIMEOUT_MS = 120_000;
// ---------------------------------------------------------------------------
// System prompt (ported from LG take_screenshot.py)
// ---------------------------------------------------------------------------
const SYSTEM_PROMPT = `You are given one or more screenshots of spreadsheet ranges. Analyze them according to the query.

Strengths -- you are good at:
- Column widths, row heights, and general layout/spacing
- Overall appearance: fonts, text size, bold/italic, alignment
- Identifying colors (background fills, text colors, borders) by approximate name
- Reading cell values and text content in small-to-medium ranges

Limitations -- be honest about:
- Precise cell coordinates can be inaccurate for large ranges
- You can identify colors visually but CANNOT determine exact hex codes or RGB values -- recommend programmatic methods for precise color extraction

Screenshot capture caveats -- the screenshots may have artifacts:
- The image is cropped and is not the same as the purported range, say so explicitly.
- Frozen panes (pinned header rows/columns) may appear in the screenshot even if they are outside the requested range. Look for a freeze line (thin gray/blue divider) and note if frozen headers are present.
- If the screenshot looks wrong (blank, garbled, or clearly not matching the requested range), flag it -- do not treat it as ground truth.

Rules:
- Be clear and concise -- your output is consumed by an agent, not a human. Minimize tokens.
- Answer the query directly. Do not repeat the query or add preamble.
- If the screenshot appears incomplete, cropped, or contain artifacts, you MUST let the main agent know
- If multiple screenshots are provided, reference them by sheet name and range
- Report exactly what you see -- do not infer data that is not visible
- Format into markdown objects like tables or lists only when they add clarity, not by default`;
const DESCRIPTION = `Take screenshots of spreadsheet ranges and analyze them with a vision LLM.
Use this to understand and verify non-data spreadsheet elements that are otherwise difficult to access or understand via code execution. \
For example: complex formatting, chart rendering, pivots, sparklines, understand layout, or see what the user sees.
Do not use this for reading cell values or data, and never as a replacement for code execution. \
Do not take screenshots bigger than 40 rows x 10 columns per range.`;
// ---------------------------------------------------------------------------
// Schema
// ---------------------------------------------------------------------------
const RangeSchema = Type.Object({
    sheet: Type.String({ description: 'Sheet name to capture.' }),
    cell_range: Type.String({ description: 'Range to capture, e.g. "A1:G10".' })
});
const schema = Type.Object({
    ranges: Type.Array(RangeSchema, {
        description: 'List of sheet:range pairs to capture.',
        minItems: 1,
        maxItems: 10
    }),
    query: Type.String({
        description: 'What to analyze about the screenshots.',
        minLength: 1
    }),
    model: Type.Optional(Type.String({
        description: `Vision model to use for analysis. Default: '${DEFAULT_VISION_MODEL}'.`
    }))
});
// ---------------------------------------------------------------------------
// Default operations (CLI mode)
// ---------------------------------------------------------------------------
/**
 * Fallback captureScreenshots — returns empty when no engine or client is available.
 */
async function defaultCaptureScreenshots(_ranges, _signal) {
    // Return empty array to trigger the "no valid screenshots" error path.
    // The tool will produce a clear error message.
    return [];
}
/**
 * Default analyzeWithVisionLLM that calls the Shortcut LLM proxy.
 *
 * The proxy streams SSE text deltas. This helper is responsible for converting
 * screenshot PNGs into model content blocks, accumulating stream text, and
 * translating proxy failures into tool-facing errors.
 */
async function defaultAnalyzeWithVisionLLM(screenshots, query, model, shortcutLlmProxy, signal) {
    // Build content blocks for the vision LLM
    const contentBlocks = [];
    for (let i = 0; i < screenshots.length; i++) {
        const ss = screenshots[i];
        if (!ss.data)
            continue;
        // Image as base64 data URI (OpenAI-compatible format used by the proxy for Gemini)
        contentBlocks.push({
            type: 'image_url',
            image_url: { url: `data:image/png;base64,${ss.data}` }
        });
        contentBlocks.push({
            type: 'text',
            text: `[screenshot_${i + 1} purported range: ${ss.sheet}!${ss.cell_range}]`
        });
    }
    contentBlocks.push({ type: 'text', text: `Query: ${query}` });
    const messages = [
        { role: 'system', content: SYSTEM_PROMPT },
        { role: 'human', content: contentBlocks }
    ];
    let response;
    try {
        response = await shortcutLlmProxy.stream({
            model,
            messages,
            max_tokens: SCREENSHOT_LLM_MAX_TOKENS,
            effort_level: 'low',
            thinking_type: 'off'
        }, { signal: signal ?? AbortSignal.timeout(SCREENSHOT_LLM_TIMEOUT_MS) });
    }
    catch (e) {
        throw mapShortcutLlmProxyError(e);
    }
    let text = '';
    await readShortcutSSE(response, async (event, payload) => {
        switch (event) {
            case 'text_delta':
                text +=
                    typeof payload?.delta === 'string'
                        ? payload.delta
                        : '';
                break;
            case 'error':
                throw new Error(payload?.message || 'LLM stream failed');
            default:
                break;
        }
    });
    if (!text) {
        throw new Error('Vision LLM returned empty response');
    }
    return { text };
}
// ---------------------------------------------------------------------------
// Default operations instance
// ---------------------------------------------------------------------------
export const defaultOperations = {
    captureScreenshots: defaultCaptureScreenshots,
    analyzeWithVisionLLM: defaultAnalyzeWithVisionLLM
};
// ---------------------------------------------------------------------------
// Tool factory
// ---------------------------------------------------------------------------
export function createTakeScreenshotTool(operations = defaultOperations) {
    return {
        name: TOOL_NAME,
        label: 'Take Screenshot',
        description: DESCRIPTION,
        parameters: schema,
        async execute(toolCallId, { ranges, query, model }, signal, _onUpdate, ctx) {
            const selectedModel = model || DEFAULT_VISION_MODEL;
            const extCtx = ctx;
            if (!extCtx?.shortcutLlmProxy) {
                throw new Error('Not authenticated. Please log in first.');
            }
            // Keep capture separate from analysis so host-specific capture failures
            // can return a clear tool result without contacting the vision model.
            const screenshots = await operations.captureScreenshots(ranges, signal ?? undefined);
            // Filter to valid screenshots (those with data)
            const validScreenshots = screenshots.filter((ss) => ss.data);
            if (validScreenshots.length === 0) {
                const rangesStr = ranges.map((r) => `${r.sheet}!${r.cell_range}`).join(', ');
                return {
                    content: [
                        {
                            type: 'text',
                            text: `Screenshot capture failed or is not supported on this platform. Ranges: ${rangesStr}`
                        }
                    ],
                    details: { error: 'no_screenshots_captured' }
                };
            }
            // Vision failures are returned as tool output so the agent can recover or
            // choose a different inspection strategy in the same turn.
            try {
                const result = await operations.analyzeWithVisionLLM(validScreenshots, query, selectedModel, extCtx.shortcutLlmProxy, signal ?? undefined);
                const rangesSummary = validScreenshots
                    .map((ss) => `${ss.sheet}!${ss.cell_range}`)
                    .join(', ');
                return {
                    content: [{ type: 'text', text: result.text }],
                    details: {
                        model: selectedModel,
                        screenshotCount: validScreenshots.length,
                        ranges: rangesSummary,
                        usage: result.usage
                    }
                };
            }
            catch (e) {
                const msg = e instanceof Error ? e.message : String(e);
                return {
                    content: [
                        {
                            type: 'text',
                            text: `Screenshot analysis failed: ${msg}`
                        }
                    ],
                    details: { error: 'vision_llm_failed', model: selectedModel }
                };
            }
        }
    };
}
//# sourceMappingURL=take-screenshot.js.map