import { getDebug } from "@midscene/shared/logger";
import { transformHotkeyInput } from "@midscene/shared/us-keyboard-layout";
import { assert } from "@midscene/shared/utils";
import { actionParser } from "@ui-tars/action-parser";
import { getSummary, getUiTarsPlanningPrompt } from "./prompt/ui-tars-planning.mjs";
import { AIResponseParseError, callAIWithStringResponse } from "./service-caller/index.mjs";
const debug = getDebug('ui-tars-planning');
const warnLog = getDebug('ui-tars-planning', {
    console: true
});
const bboxSize = 10;
const pointToBbox = (point, width, height)=>[
        Math.round(Math.max(point.x - bboxSize / 2, 0)),
        Math.round(Math.max(point.y - bboxSize / 2, 0)),
        Math.round(Math.min(point.x + bboxSize / 2, width)),
        Math.round(Math.min(point.y + bboxSize / 2, height))
    ];
async function uiTarsPlanning(userInstruction, options) {
    const { conversationHistory, context, modelConfig, actionContext } = options;
    const { uiTarsModelVersion } = modelConfig;
    let instruction = userInstruction;
    if (actionContext) instruction = `<high_priority_knowledge>${actionContext}</high_priority_knowledge>\n<user_instruction>${userInstruction}</user_instruction>`;
    const systemPrompt = getUiTarsPlanningPrompt() + instruction;
    const screenshotBase64 = context.screenshot.base64;
    conversationHistory.append({
        role: 'user',
        content: [
            {
                type: 'image_url',
                image_url: {
                    url: screenshotBase64
                }
            }
        ]
    });
    const res = await callAIWithStringResponse([
        {
            role: 'user',
            content: systemPrompt
        },
        ...conversationHistory.snapshot()
    ], modelConfig);
    let convertedText;
    let parsed;
    try {
        convertedText = convertBboxToCoordinates(res.content);
        const { size } = context;
        const parseResult = actionParser({
            prediction: convertedText,
            factor: [
                1000,
                1000
            ],
            screenContext: {
                width: size.width,
                height: size.height
            },
            modelVer: uiTarsModelVersion
        });
        parsed = parseResult.parsed;
    } catch (parseError) {
        const errorMessage = parseError instanceof Error ? parseError.message : String(parseError);
        throw new AIResponseParseError(`Parse error: ${errorMessage}`, JSON.stringify(res.content, void 0, 2), res.usage);
    }
    const { size } = context;
    debug('ui-tars modelVer', uiTarsModelVersion, ', parsed', JSON.stringify(parsed));
    const transformActions = [];
    const unhandledActions = [];
    let shouldContinue = true;
    parsed.forEach((action)=>{
        const actionType = (action.action_type || '').toLowerCase();
        if ('click' === actionType) {
            assert(action.action_inputs.start_box, 'start_box is required');
            const point = getPoint(action.action_inputs.start_box, size);
            const locate = {
                prompt: action.thought || '',
                bbox: pointToBbox({
                    x: point[0],
                    y: point[1]
                }, size.width, size.height)
            };
            transformActions.push({
                type: 'Tap',
                param: {
                    locate: locate
                }
            });
        } else if ('left_double' === actionType) {
            assert(action.action_inputs.start_box, 'start_box is required');
            const point = getPoint(action.action_inputs.start_box, size);
            const locate = {
                prompt: action.thought || '',
                bbox: pointToBbox({
                    x: point[0],
                    y: point[1]
                }, size.width, size.height)
            };
            transformActions.push({
                type: 'DoubleClick',
                param: {
                    locate: locate
                },
                thought: action.thought || ''
            });
        } else if ('right_single' === actionType) {
            assert(action.action_inputs.start_box, 'start_box is required');
            const point = getPoint(action.action_inputs.start_box, size);
            const locate = {
                prompt: action.thought || '',
                bbox: pointToBbox({
                    x: point[0],
                    y: point[1]
                }, size.width, size.height)
            };
            transformActions.push({
                type: 'RightClick',
                param: {
                    locate: locate
                },
                thought: action.thought || ''
            });
        } else if ('drag' === actionType) {
            assert(action.action_inputs.start_box, 'start_box is required');
            assert(action.action_inputs.end_box, 'end_box is required');
            const startPoint = getPoint(action.action_inputs.start_box, size);
            const endPoint = getPoint(action.action_inputs.end_box, size);
            transformActions.push({
                type: 'DragAndDrop',
                param: {
                    from: {
                        prompt: action.thought || '',
                        bbox: pointToBbox({
                            x: startPoint[0],
                            y: startPoint[1]
                        }, size.width, size.height)
                    },
                    to: {
                        prompt: action.thought || '',
                        bbox: pointToBbox({
                            x: endPoint[0],
                            y: endPoint[1]
                        }, size.width, size.height)
                    }
                },
                thought: action.thought || ''
            });
        } else if ('type' === actionType) transformActions.push({
            type: 'Input',
            param: {
                value: action.action_inputs.content
            },
            thought: action.thought || ''
        });
        else if ('scroll' === actionType) transformActions.push({
            type: 'Scroll',
            param: {
                direction: action.action_inputs.direction
            },
            thought: action.thought || ''
        });
        else if ('finished' === actionType) {
            shouldContinue = false;
            transformActions.push({
                type: 'Finished',
                param: {},
                thought: action.thought || ''
            });
        } else if ('hotkey' === actionType) if (action.action_inputs.key) {
            const keys = transformHotkeyInput(action.action_inputs.key);
            transformActions.push({
                type: 'KeyboardPress',
                param: {
                    keyName: keys.join('+')
                },
                thought: action.thought || ''
            });
        } else warnLog('No key found in action: hotkey. Will not perform action.');
        else if ('wait' === actionType) transformActions.push({
            type: 'Sleep',
            param: {
                timeMs: 1000
            },
            thought: action.thought || ''
        });
        else if (actionType) {
            unhandledActions.push({
                type: actionType,
                thought: action.thought || ''
            });
            debug('Unhandled action type:', actionType, 'thought:', action.thought);
        }
    });
    if (0 === transformActions.length) {
        const errorDetails = [];
        if (0 === parsed.length) {
            errorDetails.push('Action parser returned no actions');
            if (res.content.includes('Thought:') && !res.content.includes('Action:')) errorDetails.push('Response contains "Thought:" but missing "Action:" line');
            else errorDetails.push('Response may be malformed or empty');
        }
        if (unhandledActions.length > 0) {
            const types = unhandledActions.map((a)=>a.type).join(', ');
            errorDetails.push(`Unhandled action types: ${types}`);
        }
        const errorMessage = [
            'No actions found in UI-TARS response.',
            ...errorDetails
        ].join('\n');
        throw new AIResponseParseError(errorMessage, JSON.stringify(res.content, void 0, 2), res.usage);
    }
    debug('transformActions', JSON.stringify(transformActions, null, 2));
    const log = getSummary(res.content);
    conversationHistory.append({
        role: 'assistant',
        content: log
    });
    return {
        actions: transformActions,
        log,
        usage: res.usage,
        rawResponse: JSON.stringify(res.content, void 0, 2),
        shouldContinuePlanning: shouldContinue
    };
}
function convertBboxToCoordinates(text) {
    const pattern = /<bbox>(\d+)\s+(\d+)\s+(\d+)\s+(\d+)<\/bbox>/g;
    function replaceMatch(match, x1, y1, x2, y2) {
        const x1Num = Number.parseInt(x1, 10);
        const y1Num = Number.parseInt(y1, 10);
        const x2Num = Number.parseInt(x2, 10);
        const y2Num = Number.parseInt(y2, 10);
        const x = Math.floor((x1Num + x2Num) / 2);
        const y = Math.floor((y1Num + y2Num) / 2);
        return `(${x},${y})`;
    }
    const cleanedText = text.replace(/\[EOS\]/g, '');
    return cleanedText.replace(pattern, replaceMatch).trim();
}
function getPoint(startBox, size) {
    const [x, y] = JSON.parse(startBox);
    return [
        x * size.width,
        y * size.height
    ];
}
export { uiTarsPlanning };

//# sourceMappingURL=ui-tars-planning.mjs.map