/**
 * Step Utilities — pure functions for parsing step data, planner responses,
 * and tool call information. No external state dependencies.
 *
 * Extracted from extension.ts for maintainability.
 */

export function extractPlannerText(step: any): string | null {
    if (!step) { return null; }

    const fs = require('fs');
    const path = require('path');
    const dumpPath = path.join(require('os').homedir(), '.gemini', 'antigravity', 'bridge', 'planner_dump.json');
    try {
        fs.writeFileSync(dumpPath, JSON.stringify(step, null, 2), {flag: 'a'});
    } catch (e) {}

    // Fields to SKIP — not user-facing content
    const SKIP_FIELDS = new Set([
        'thinking', 'thinkingSignature', 'stopReason', 'type', 'status', 'metadata',
        'ephemeralMessage', 'generatorModel', 'requestedModel',
        'executionId', 'sourceTrajectoryStepInfo', 'stepIndex',
        'viewableAt', 'createdAt', 'finishedGeneratingAt',
        'lastCompletedChunkAt', 'source', 'stepGenerationVersion'
    ]);

    // plannerResponse can be string or object
    const pr = step.plannerResponse || step.step?.plannerResponse;
    if (typeof pr === 'string' && pr.length > 10) {
        return filterEphemeral(pr);
    }
    if (pr && typeof pr === 'object') {
        const text = pr.content || pr.text || pr.summary || pr.message || pr.response || pr.output;
        if (typeof text === 'string' && text.length > 10) {
            return filterEphemeral(text);
        }
        for (const key of Object.keys(pr)) {
            if (SKIP_FIELDS.has(key)) continue;
            const val = pr[key];
            if (typeof val === 'string' && val.length > 50) { // Higher threshold
                const filtered = filterEphemeral(val);
                if (filtered) {
                    return filtered;
                }
            }
        }
    }

    // Fallback: nested fields not caught by top-level string iteration
    if (step.content?.parts) {
        let txt = '';
        for (const p of step.content.parts) { if (p?.text) txt += p.text; }
        if (txt.length > 10) return filterEphemeral(txt);
    }
    if (step.parts) {
        let txt = '';
        for (const p of step.parts) { if (p?.text) txt += p.text; }
        if (txt.length > 10) return filterEphemeral(txt);
    }
    if (step.metadata?.text && step.metadata.text.length > 10) {
        return filterEphemeral(step.metadata.text);
    }
    if (step.rawOutput) {
        const txt = typeof step.rawOutput === 'string' ? step.rawOutput : JSON.stringify(step.rawOutput);
        if (txt.length > 10) return filterEphemeral(txt);
    }

    return null;
}

/** Filter out system ephemeral messages and non-content strings. */
export function filterEphemeral(text: string): string | null {
    if (!text || text.length < 10) { return null; }
    
    // Strip ephemeral system blocks entirely without dropping the user-facing text
    let cleaned = text;
    
    // Target the specific Gemini system prompt injection format:
    // "The following is an <EPHEMERAL_MESSAGE>... </ephemeral_message>"
    // Make sure to match case-insensitively and dotAll
    cleaned = cleaned.replace(/The following is an <EPHEMERAL_MESSAGE>[\s\S]*?(?:<\/ephemeral_message>|<\/EPHEMERAL_MESSAGE>|$)/gi, '');
    
    // Target standard blocks
    cleaned = cleaned.replace(/<EPHEMERAL_MESSAGE>[\s\S]*?<\/EPHEMERAL_MESSAGE>/gi, '');
    cleaned = cleaned.replace(/<ephemeral_message>[\s\S]*?<\/ephemeral_message>/gi, '');
    
    // Strip other known reminder blocks (if they exist as XML-like tags, just remove them)
    cleaned = cleaned.replace(/<artifact_reminder>[\s\S]*?<\/artifact_reminder>/gi, '');
    cleaned = cleaned.replace(/<active_task_reminder>[\s\S]*?<\/active_task_reminder>/gi, '');
    cleaned = cleaned.replace(/<no_active_task_reminder>[\s\S]*?<\/no_active_task_reminder>/gi, '');
    
    cleaned = cleaned.trim();
    if (!cleaned || cleaned.length < 10) { return null; }

    // Skip base64/crypto strings (no spaces, mostly alphanumeric)
    if (!cleaned.includes(' ') && /^[A-Za-z0-9+/=_\-]{50,}$/.test(cleaned)) { return null; }
    
    return cleaned;
}

/** Extract human-readable command from a tool call step's data. */
export function extractToolCommand(stepData: any): string {
    // Try common step data shapes from protobuf
    if (stepData.runCommand) {
        return stepData.runCommand.commandLine || stepData.runCommand.command || 'Run Command';
    }
    if (stepData.writeToFile) {
        const target = stepData.writeToFile.targetFile || stepData.writeToFile.filePath || 'file';
        return `Write: ${target.split(/[\\/]/).pop()}`;
    }
    if (stepData.codeAction) {
        const fp = stepData.codeAction.filePath || '';
        return `Edit: ${fp.split(/[\\/]/).pop() || 'file'}`;
    }
    if (stepData.replaceFileContent || stepData.multiReplaceFileContent) {
        const d = stepData.replaceFileContent || stepData.multiReplaceFileContent;
        const fp = d.targetFile || d.filePath || '';
        return `Edit: ${fp.split(/[\\/]/).pop() || 'file'}`;
    }
    if (stepData.sendCommandInput) {
        return `Send Input: ${(stepData.sendCommandInput.input || '').substring(0, 50)}`;
    }
    // Generic fallback: use first key name
    const keys = Object.keys(stepData).filter(k => k !== 'status' && k !== 'stepStatus');
    return keys.length > 0 ? keys[0] : 'Unknown tool call';
}

/** Extract description from a tool call step for Discord display. */
export function extractToolDescription(stepData: any, sessionTitle: string, stepIndex: number): string {
    const parts = [`Step #${stepIndex}`, `Session: "${sessionTitle}"`];
    // Try to get code/command content for context
    if (stepData.runCommand) {
        const cmd = stepData.runCommand.commandLine || stepData.runCommand.command || '';
        if (cmd) parts.push(`Command: ${cmd.substring(0, 200)}`);
    }
    if (stepData.writeToFile?.targetFile) {
        parts.push(`File: ${stepData.writeToFile.targetFile}`);
    }
    if (stepData.codeAction?.filePath) {
        parts.push(`File: ${stepData.codeAction.filePath}`);
    }
    return parts.join('\n');
}
