/**
 * Claude Agent SDK 引擎适配器。
 *
 * Phase 0：以 createQuery() 形式包装现有 createForgeQuery 调用，把所有
 * Claude SDK 的工厂调用收敛到本文件。消费方（conversation-engine / chat route /
 * memory-flush / cron executor）通过 EngineRegistry 获取本适配器，不再直接
 * 调用 createForgeQuery。
 *
 * 行为零变化：createQuery 仅做 1:1 转发。
 */

import { createForgeQuery } from '@/lib/engine/adapters/claude/client'
import type { ForgeQueryOptions } from '@/lib/engine/adapters/claude/client'
import type { CanUseTool, PermissionResult, Query } from '@anthropic-ai/claude-agent-sdk'
import { MessageMapper } from './message-mapper'
import { dispatchSseEvent } from './stream-dispatcher'
import { EngineRegistry } from '../../registry'
import type { AgentEngineAdapter } from '../../types'
import type { AgentCallbacks, AgentRequest, AgentResult, PermissionRiskLevel } from '../../agent-types'

/** Claude 引擎标识，写入 sessions.backend */
export const CLAUDE_ENGINE_ID = 'claude'

class ClaudeAdapter implements AgentEngineAdapter {
  readonly id = CLAUDE_ENGINE_ID
  readonly name = 'Claude Agent SDK'

  createQuery(options: ForgeQueryOptions): Query {
    return createForgeQuery(options)
  }

  /**
   * Phase 0.5 目标方法：消费 SDK 流，通过统一 AgentCallbacks 输出，返回 AgentResult。
   * 复用 MessageMapper 的精细映射（agent_content/tool_raw_result/web_search），
   * 对外不暴露 SDKMessage。零行为变化（与原 consumeStream/drainQuery 等价）。
   */
  async process(request: AgentRequest, callbacks: AgentCallbacks): Promise<AgentResult> {
    // 权限合成：request.canUseTool 优先；否则若提供 onPermissionRequest，合成 canUseTool
    const canUseTool = request.canUseTool
        ?? (callbacks.onPermissionRequest ? this.synthesizeCanUseTool(callbacks) : undefined)

    const q = createForgeQuery({ ...request, canUseTool })
    const mapper = new MessageMapper()
    let firstEventSent = false

    // 不 catch for await —— 让流异常（abort / "Session ID already in use" 等）向上传播，
    // 由 processMessage（IM fallback）/ route.ts（桌面 error emit）处理，与原 consumeStream/drainQuery 一致。
    // SDK 返回的 error result（软错误，不 throw）由 dispatcher 的 onError 回调处理。
    for await (const msg of q) {
      if (!firstEventSent) {
        firstEventSent = true
        try { await callbacks.onFirstEvent?.() } catch (err) { console.warn('[ClaudeAdapter] onFirstEvent error:', err instanceof Error ? err.message : err) }
      }
      const events = mapper.mapMessage(msg)
      for (const event of events) {
        await dispatchSseEvent(event, callbacks)
      }
    }

    const blocks = mapper.getBlocks()
    const text = blocks
        .filter((b): b is { type: 'text'; text: string } => b.type === 'text')
        .map((b) => b.text)
        .join('')

    // 终结回调（顺序：sdkSession → usage → block → done）
    if (mapper.sdkSessionId) {
      try { await callbacks.onSdkSession?.(mapper.sdkSessionId) } catch { /* ignore */ }
    }
    if (mapper.inputTokens || mapper.outputTokens) {
      try { await callbacks.onUsage?.(mapper.inputTokens, mapper.outputTokens) } catch { /* ignore */ }
    }
    for (const block of blocks) {
      try { await callbacks.onBlock?.(block) } catch { /* ignore */ }
    }

    const result: AgentResult = {
      text,
      blocks,
      inputTokens: mapper.inputTokens,
      outputTokens: mapper.outputTokens,
      sdkSessionId: mapper.sdkSessionId,
      subtype: mapper.resultSubtype,
      finishReason: mapper.finishReason,
      totalCostUsd: mapper.totalCostUsd,
      durationMs: mapper.durationMs,
      numTurns: mapper.numTurns,
    }
    try { await callbacks.onDone?.(result) } catch { /* ignore */ }
    return result
  }

  private classifyPermissionRisk(toolName: string, input: Record<string, unknown>): PermissionRiskLevel {
    if (toolName === 'Read' || toolName === 'Glob' || toolName === 'Grep' || toolName === 'LS') return 'low'

    if (toolName === 'Bash') {
      const command = typeof input.command === 'string' ? input.command : ''
      if (/\b(rm|sudo|chmod|chown|curl|wget|ssh|scp|dd)\b|>|\|\s*(sh|bash)\b/i.test(command)) return 'high'
      return 'medium'
    }

    if (toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') {
      const path = typeof input.file_path === 'string' ? input.file_path : ''
      if (/^\/(etc|usr|bin|sbin|System|Library)\b/.test(path) || /\.ssh|\.env|credentials|secret/i.test(path)) return 'high'
      return 'medium'
    }

    return 'medium'
  }

  private describePermissionRequest(toolName: string, input: Record<string, unknown>): string {
    if (toolName === 'Bash' && typeof input.command === 'string') return `Run command: ${input.command}`
    if ((toolName === 'Write' || toolName === 'Edit' || toolName === 'MultiEdit') && typeof input.file_path === 'string') {
      return `${toolName} file: ${input.file_path}`
    }
    return `${toolName} wants to execute`
  }

  /**
   * 把 onPermissionRequest 回调合成 SDK 的 canUseTool（复刻 conversation-engine 117-137 行的包装）。
   * IM 聚合适配器补 chatId/channelType/senderId 后再调 ConversationCallbacks.onPermissionRequest。
   */
  private synthesizeCanUseTool(callbacks: AgentCallbacks): CanUseTool {
    return async (toolName, input, opts) => {
      const typedInput = input as Record<string, unknown>
      const riskLevel = this.classifyPermissionRisk(toolName, typedInput)
      const description = this.describePermissionRequest(toolName, typedInput)
      const decision = await callbacks.onPermissionRequest!({
        toolName,
        input: typedInput,
        toolUseId: opts.toolUseID,
        riskLevel,
        description,
      })
      if (decision === 'allow') {
        return { behavior: 'allow', updatedPermissions: opts.suggestions } as PermissionResult
      }
      return { behavior: 'deny', message: `Permission denied for ${toolName}` } as PermissionResult
    }
  }

  // Phase 2+: 接入 BUILTIN_MODELS / provider catalog
  // async getModels(): Promise<EngineModel[]> { ... }
}

// 模块加载即注册到全局引擎表
EngineRegistry.register(new ClaudeAdapter())
