// ============================================
// Anthropic Messages API — Direct Client (v2: tool_use)
// Replaces CLAUDE_CLI_PROXY_URL when ANTHROPIC_API_KEY is set
// Priority: ANTHROPIC_API_KEY > CLAUDE_CLI_PROXY_URL > fallback
// ============================================

const ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';
const ANTHROPIC_MODEL = 'claude-sonnet-4-20250514';
const ANTHROPIC_VERSION = '2023-06-01';
const MAX_TOKENS = 2048;
const TIMEOUT_MS = 120000; // 120s

// ── System Prompts ──────────────────────────

const CHAT_SYSTEM_PROMPT = `You are the customer support assistant for E-Claw (Claw Live Wallpaper), an Android live wallpaper app where AI bots control animated characters on the user's phone screen.

Key product knowledge:
- Each device supports up to 4 entity slots (0-3), each independently bindable to an AI bot
- Bots bind via a 5-minute binding code, then register a webhook for real-time push notifications
- Bots reply by calling POST /api/transform with their deviceId, entityId, and botSecret
- Entity types: LOBSTER (slots 0,2) and PIG (slots 1,3)
- The app has: Chat, Mission Control dashboard, File Manager, Schedule, Settings, Contacts
- Common issues: expired binding codes, webhook registration failures, SETUP_PASSWORD blocking auth
- Web portal at eclawbot.com provides admin features and the same chat interface

Your behavior:
- Be concise, friendly, and helpful
- When troubleshooting, ask for specific error messages
- For binding issues, guide users through the binding flow step by step
- For feature questions, explain clearly with examples
- If you don't know something, say so honestly
- Respond in the same language the user writes in (Chinese or English)
- Do NOT fabricate API endpoints or features that don't exist

Customer Service Tools:
You have device lookup tools to help diagnose user issues:
- lookup_device: Look up device info (entities, platform, version, webhook status) by deviceId. Returns deviceSecret too.
- query_device_logs: Query recent server logs for a device (with optional category/level filters)
- lookup_user_by_email: Find a user account and their associated device (deviceId + deviceSecret) by email address

IMPORTANT — Proactive Device Lookup:
- The current user's device credentials (deviceId + deviceSecret) are included in the session context below.
- On the FIRST message of a new conversation, ALWAYS call lookup_device with the user's deviceId to get their full device state (entities, webhook status, recent errors).
- When a user describes a problem, immediately use query_device_logs to check recent errors — don't ask for details you can look up.
- If a user identifies themselves by email, use lookup_user_by_email to find their device.
- Always reference the user's deviceId and deviceSecret in your responses when relevant (e.g., for API troubleshooting), so the user can copy-paste them.

GitHub Issue Management (CRITICAL):
You have TWO GitHub tools: create_github_issue and close_github_issue.
- create_github_issue: use when a user reports a bug or suggests a feature
- close_github_issue: use when a user EXPLICITLY asks to close a specific issue by number
IMPORTANT: You DO have permission to close GitHub issues using the close_github_issue tool.
NEVER say you cannot close GitHub issues. When asked to close an issue, ALWAYS call the close_github_issue tool immediately.`;

const ANALYZE_SYSTEM_PROMPT = `You are a diagnostic engine for E-Claw (Claw Live Wallpaper) binding and webhook issues.

Analyze the provided error messages, server logs, and handshake failures to produce a diagnosis.

Your response MUST be valid JSON with this structure:
{
  "diagnosis": "Clear explanation of the root cause",
  "suggested_steps": ["Step 1: ...", "Step 2: ...", "Step 3: ..."],
  "confidence": 0.8
}

Common issues:
- expired_code: Binding codes expire after 5 minutes
- localhost_rejected: Bot's webhook URL contains localhost (must be public URL)
- http_401: SETUP_PASSWORD is set but bot didn't provide setup_username/setup_password
- tool_not_available: Gateway doesn't allow the sessions_send tool
- pairing_required: Gateway requires re-pairing after restart
- connection_failed: Webhook URL is unreachable from the server

Provide actionable, specific fix instructions with exact API calls or commands.`;

// ── Tool Definitions ────────────────────────

const GITHUB_TOOLS = [
    {
        name: 'create_github_issue',
        description: 'Create a new GitHub issue for bug reports or feature requests. Use when a user reports a problem or suggests a feature.',
        input_schema: {
            type: 'object',
            properties: {
                title: { type: 'string', description: 'Issue title (concise, descriptive)' },
                body: { type: 'string', description: 'Issue body with reproduction steps, expected behavior, etc.' },
                labels: {
                    type: 'array',
                    items: { type: 'string' },
                    description: 'Labels for the issue (e.g. "bug", "enhancement", "ux")'
                }
            },
            required: ['title', 'body']
        }
    },
    {
        name: 'close_github_issue',
        description: 'Close an existing GitHub issue. Use when a user explicitly asks to close a specific issue by number.',
        input_schema: {
            type: 'object',
            properties: {
                issue_number: { type: 'integer', description: 'The GitHub issue number to close' },
                comment: { type: 'string', description: 'Optional comment explaining why the issue is being closed' }
            },
            required: ['issue_number']
        }
    }
];

// ── Customer Service Tools ──────────────────
const CUSTOMER_SERVICE_TOOLS = [
    {
        name: 'lookup_device',
        description: 'Look up device information including entities, platform, app version, and webhook status. Use this to check device state when troubleshooting.',
        input_schema: {
            type: 'object',
            properties: {
                device_id: { type: 'string', description: 'The device ID to look up' }
            },
            required: ['device_id']
        }
    },
    {
        name: 'query_device_logs',
        description: 'Query recent server logs for a device. Returns logs filtered by category, level, and time range.',
        input_schema: {
            type: 'object',
            properties: {
                device_id: { type: 'string', description: 'The device ID to query logs for' },
                category: {
                    type: 'string',
                    description: 'Log category filter: bind, unbind, transform, broadcast, broadcast_push, speakto_push, client_push, push_error',
                    enum: ['bind', 'unbind', 'transform', 'broadcast', 'broadcast_push', 'speakto_push', 'client_push', 'push_error']
                },
                level: { type: 'string', description: 'Log level filter', enum: ['info', 'warn', 'error'] },
                hours: { type: 'number', description: 'How many hours back to query (default: 24, max: 72)' },
                limit: { type: 'integer', description: 'Max number of log entries (default: 30, max: 100)' }
            },
            required: ['device_id']
        }
    },
    {
        name: 'lookup_user_by_email',
        description: 'Find a user account and their associated device by email address. Useful when a user identifies themselves by email.',
        input_schema: {
            type: 'object',
            properties: {
                email: { type: 'string', description: 'The email address to look up' }
            },
            required: ['email']
        }
    }
];

// ── Helpers ─────────────────────────────────

/**
 * Convert internal history to Anthropic Messages API format.
 * Ensures strict user/assistant alternation (Anthropic requirement).
 */
function convertHistory(history) {
    if (!Array.isArray(history) || history.length === 0) return [];

    const result = [];
    for (const msg of history) {
        const role = msg.role === 'user' ? 'user' : 'assistant';
        const content = typeof msg.content === 'string' ? msg.content : String(msg.content || '');
        if (!content.trim()) continue;

        // Merge consecutive same-role messages
        if (result.length > 0 && result[result.length - 1].role === role) {
            result[result.length - 1].content += '\n' + content;
        } else {
            result.push({ role, content });
        }
    }

    // Anthropic requires first message to be 'user'
    while (result.length > 0 && result[0].role !== 'user') {
        result.shift();
    }

    return result;
}

/**
 * Convert images to Anthropic vision format.
 * Input:  [{ data: base64, mimeType: "image/jpeg" }]
 * Output: [{ type: "image", source: { type: "base64", media_type, data } }]
 */
function convertImages(images) {
    if (!Array.isArray(images)) return [];
    return images
        .filter(img => img && img.data && img.mimeType)
        .slice(0, 3)
        .map(img => ({
            type: 'image',
            source: {
                type: 'base64',
                media_type: img.mimeType,
                data: img.data
            }
        }));
}

/**
 * Build the user message content (text + optional images).
 */
function buildUserContent(message, images) {
    const imageBlocks = convertImages(images);
    if (imageBlocks.length === 0) return message;

    // Multi-part content: images first, then text
    return [
        ...imageBlocks,
        { type: 'text', text: message }
    ];
}

/**
 * Call Anthropic Messages API (with optional tool use).
 */
async function callAnthropic(systemPrompt, messages, tools) {
    const apiKey = process.env.ANTHROPIC_API_KEY;
    if (!apiKey) throw new Error('ANTHROPIC_API_KEY not set');

    const body = {
        model: ANTHROPIC_MODEL,
        max_tokens: MAX_TOKENS,
        system: systemPrompt,
        messages
    };
    if (tools && tools.length > 0) {
        console.log(`[Anthropic] Sending ${tools.length} tool(s): ${tools.map(t => t.name).join(', ')}`);
        body.tools = tools;
    }

    const response = await fetch(ANTHROPIC_API_URL, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'x-api-key': apiKey,
            'anthropic-version': ANTHROPIC_VERSION
        },
        body: JSON.stringify(body),
        signal: AbortSignal.timeout(TIMEOUT_MS)
    });

    if (!response.ok) {
        const errBody = await response.text().catch(() => '');
        throw new Error(`Anthropic API ${response.status}: ${errBody.slice(0, 300)}`);
    }

    return response.json();
}

/**
 * Extract text response and tool use calls from Anthropic response.
 */
function parseResponse(result) {
    console.log(`[Anthropic] Response stop_reason=${result.stop_reason}, content_blocks=${(result.content || []).length}, types=${(result.content || []).map(b => b.type).join(',')}`);
    let responseText = '';
    const actions = [];

    for (const block of (result.content || [])) {
        if (block.type === 'text') {
            responseText += block.text;
        } else if (block.type === 'tool_use') {
            if (block.name === 'create_github_issue') {
                actions.push({
                    type: 'create_issue',
                    title: block.input.title,
                    body: block.input.body,
                    labels: block.input.labels || ['bug']
                });
            } else if (block.name === 'close_github_issue') {
                actions.push({
                    type: 'close_issue',
                    issue_number: block.input.issue_number,
                    comment: block.input.comment
                });
            }
        }
    }

    return {
        response: responseText || 'Action executed.',
        actions: actions.length > 0 ? actions : null
    };
}

// ── Diagnostics Formatter ────────────────────

/**
 * Format device diagnostics into a human-readable block for the system prompt.
 * Covers: platform/version, entity slot states, recent errors (24h), recent
 * key activity (1h), and handshake failures (1h).
 */
function formatDiagnostics(diag) {
    if (!diag) return null;
    const parts = [];

    // Platform + version
    if (diag.platform || diag.appVersion) {
        const ver = diag.appVersion ? ` (v${diag.appVersion})` : '';
        parts.push(`Platform: ${diag.platform || 'unknown'}${ver}`);
    }

    // Entity slot states
    if (diag.entityStates?.length > 0) {
        const lines = diag.entityStates.map(e => {
            if (e.bound) {
                const webhook = e.hasWebhook ? '[webhook registered]' : '[no webhook]';
                return `  Slot ${e.slot} (${e.type}): bound as "${e.name}" ${webhook}`;
            }
            return `  Slot ${e.slot} (${e.type}): unbound`;
        });
        parts.push('Entity slots:\n' + lines.join('\n'));
    }

    // Recent errors — 24h
    if (diag.recentErrors?.length > 0) {
        const lines = diag.recentErrors.map(e => {
            const ts = new Date(e.created_at).toISOString().slice(0, 16).replace('T', ' ');
            const meta = e.metadata ? ' — ' + JSON.stringify(e.metadata).slice(0, 100) : '';
            return `  [${ts}][${e.category}] slot_${e.entity_id ?? '-'}: ${e.message}${meta}`;
        });
        parts.push('Recent errors (24h):\n' + lines.join('\n'));
    }

    // Recent key activity — 1h (bind/unbind/push/transform)
    if (diag.recentActivity?.length > 0) {
        const lines = diag.recentActivity.map(e => {
            const ts = new Date(e.created_at).toISOString().slice(0, 16).replace('T', ' ');
            return `  [${ts}][${e.level}][${e.category}] slot_${e.entity_id ?? '-'}: ${e.message}`;
        });
        parts.push('Recent activity (1h):\n' + lines.join('\n'));
    }

    // Handshake failures — 1h
    if (diag.handshakeFailures?.length > 0) {
        const lines = diag.handshakeFailures.map(f => {
            const ts = new Date(f.created_at).toISOString().slice(0, 16).replace('T', ' ');
            return `  [${ts}] ${f.error_type}: ${f.error_message || '(no detail)'}`;
        });
        parts.push('Handshake failures (1h):\n' + lines.join('\n'));
    }

    return parts.length > 0 ? parts.join('\n\n') : null;
}

// ── Exported Functions ──────────────────────

/**
 * Universal chat — matches proxy /chat interface.
 * @param {Object} opts
 * @param {string} opts.message - Current user message
 * @param {Array}  opts.history - Conversation history [{role, content}]
 * @param {Array}  [opts.images] - Optional images [{data, mimeType}]
 * @param {Object} [opts.deviceContext] - {deviceId, page, role, email}
 * @param {Object} [opts.deviceDiagnostics] - Full device diagnostic snapshot from fetchDeviceContext()
 * @returns {Promise<{response: string, actions: Array|null}>}
 */
async function chatWithClaude({ message, history, images, deviceContext, deviceDiagnostics, toolHandlers }) {
    // Build context-enriched system prompt
    let system = CHAT_SYSTEM_PROMPT;
    if (deviceContext) {
        const ctx = [];
        if (deviceContext.deviceId) ctx.push(`Device ID: ${deviceContext.deviceId}`);
        if (deviceContext.deviceSecret) ctx.push(`Device Secret: ${deviceContext.deviceSecret}`);
        if (deviceContext.page) ctx.push(`Page: ${deviceContext.page}`);
        if (deviceContext.role) ctx.push(`User role: ${deviceContext.role}`);
        if (deviceContext.email) ctx.push(`Email: ${deviceContext.email}`);
        if (ctx.length > 0) {
            system += `\n\nCurrent session context:\n${ctx.join('\n')}`;
        }
    }

    // Append device diagnostics (errors, activity, entity states)
    const diagText = formatDiagnostics(deviceDiagnostics);
    if (diagText) {
        system += `\n\nDevice diagnostics snapshot:\n${diagText}`;
    }

    // Combine all tools: GitHub + customer service (when handlers are provided)
    const allTools = [...GITHUB_TOOLS];
    if (toolHandlers) {
        allTools.push(...CUSTOMER_SERVICE_TOOLS);
    }

    // Build messages array
    const messages = convertHistory(history || []);
    const userContent = buildUserContent(message, images);
    messages.push({ role: 'user', content: userContent });

    // Tool use loop: max 5 rounds to prevent infinite loops
    const MAX_TOOL_ROUNDS = 5;
    for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
        const result = await callAnthropic(system, messages, allTools);

        // Check if Claude wants to use tools
        const toolUseBlocks = (result.content || []).filter(b => b.type === 'tool_use');
        const csToolUses = toolHandlers
            ? toolUseBlocks.filter(b => CUSTOMER_SERVICE_TOOLS.some(t => t.name === b.name))
            : [];

        if (csToolUses.length === 0) {
            // No customer service tool calls — return final response
            return parseResponse(result);
        }

        // Execute customer service tools and feed results back
        // Add assistant message with all content blocks
        messages.push({ role: 'assistant', content: result.content });

        // Execute each tool and collect results
        const toolResults = [];
        for (const toolUse of csToolUses) {
            let toolResult;
            try {
                const handler = toolHandlers[toolUse.name];
                if (handler) {
                    toolResult = await handler(toolUse.input);
                } else {
                    toolResult = { error: `Unknown tool: ${toolUse.name}` };
                }
            } catch (err) {
                toolResult = { error: `Tool execution failed: ${err.message}` };
            }
            toolResults.push({
                type: 'tool_result',
                tool_use_id: toolUse.id,
                content: typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult)
            });
        }

        // Also handle GitHub tool calls as "not executed here" so Claude doesn't hang
        const ghToolUses = toolUseBlocks.filter(b => GITHUB_TOOLS.some(t => t.name === b.name));
        for (const ghTool of ghToolUses) {
            toolResults.push({
                type: 'tool_result',
                tool_use_id: ghTool.id,
                content: JSON.stringify({ status: 'deferred', message: 'GitHub action will be executed after response' })
            });
        }

        messages.push({ role: 'user', content: toolResults });
        console.log(`[Anthropic] Tool use round ${round + 1}: executed ${csToolUses.length} CS tool(s)`);
    }

    // If we exhausted rounds, return the last response
    const finalResult = await callAnthropic(system, messages, allTools);
    return parseResponse(finalResult);
}

/**
 * Binding analysis — matches proxy /analyze interface.
 * @param {Object} opts
 * @param {string} opts.problemDescription
 * @param {Array}  opts.errorMessages
 * @param {Array}  opts.logs - Recent server log rows
 * @param {Array}  opts.handshakeFailures
 * @param {Object} opts.deviceContext
 * @returns {Promise<{diagnosis: string, suggested_steps: string[], confidence: number}>}
 */
async function analyzeWithClaude({ problemDescription, errorMessages, logs, handshakeFailures, deviceContext }) {
    const userMessage = [
        `Problem: ${problemDescription || 'Unknown'}`,
        '',
        `Error messages:\n${(errorMessages || []).join('\n') || 'None'}`,
        '',
        `Recent server logs (last 30):\n${JSON.stringify((logs || []).slice(0, 30), null, 2)}`,
        '',
        `Handshake failures (last 10):\n${JSON.stringify((handshakeFailures || []).slice(0, 10), null, 2)}`,
        '',
        `Device context: ${JSON.stringify(deviceContext || {})}`,
        '',
        'Analyze the above and return your diagnosis as JSON.'
    ].join('\n');

    const result = await callAnthropic(ANALYZE_SYSTEM_PROMPT, [
        { role: 'user', content: userMessage }
    ]);

    const text = result.content?.[0]?.text || '';

    // Try to parse JSON from response
    try {
        // Handle markdown code blocks
        const jsonMatch = text.match(/\{[\s\S]*\}/);
        if (jsonMatch) {
            const parsed = JSON.parse(jsonMatch[0]);
            return {
                diagnosis: parsed.diagnosis || 'Analysis complete.',
                suggested_steps: Array.isArray(parsed.suggested_steps) ? parsed.suggested_steps : [],
                confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.7
            };
        }
    } catch (_) {}

    // Fallback: return raw text as diagnosis
    return {
        diagnosis: text || 'Unable to analyze the issue.',
        suggested_steps: [],
        confidence: 0.5
    };
}

module.exports = { chatWithClaude, analyzeWithClaude, callAnthropic, formatDiagnostics, CUSTOMER_SERVICE_TOOLS };
