// Derive a file operation or shell execution from a tool_use block's name +
// input. Thomas taps the model wire, so it never sees the local file/shell
// action directly — but the model's tool_use call carries the intent (which
// file, which command). This maps the common tool vocabularies (Claude Code,
// Codex, OpenClaw, Cursor, generic MCP) to a normalized {kind, op, target}.
//
// Heuristic + generic on purpose: BYOA agents pick their own tool names, so
// we match on name patterns and pull the target from the usual input keys
// rather than a fixed per-agent table. Returns null when the tool isn't a
// recognizable file/shell op or no target string is present.

export type ToolTarget = {
  kind: 'fs' | 'shell'
  op: 'edit' | 'write' | 'delete' | 'read' | 'exec'
  target: string
}

const TARGET_MAX = 2000

function firstString(input: unknown, keys: string[]): string | undefined {
  if (!input || typeof input !== 'object') return undefined
  const o = input as Record<string, unknown>
  for (const k of keys) {
    const v = o[k]
    if (typeof v === 'string' && v.trim()) return v.trim()
  }
  return undefined
}

export function deriveToolTarget(name: string, input: unknown): ToolTarget | null {
  const n = name.toLowerCase()

  // Shell execution — Bash, run_command, execute_command, run_terminal_cmd, …
  if (/\b(bash|shell|terminal)\b/.test(n) || /(^|_)(exec|run|command|cmd)(_|$)/.test(n)) {
    const cmd = firstString(input, ['command', 'cmd', 'script', 'code', 'input'])
    if (cmd) return { kind: 'shell', op: 'exec', target: cmd.slice(0, TARGET_MAX) }
    return null
  }

  // File operations — Edit, Write, MultiEdit, str_replace, apply_patch,
  // create_file, NotebookEdit, Read, view, delete, …
  const isFsName = /(edit|write|patch|replace|create|delete|remove|read|view|notebook|file)/.test(n)
  if (isFsName) {
    const path = firstString(input, [
      'file_path',
      'filePath',
      'path',
      'filename',
      'file',
      'notebook_path',
      'target_file',
    ])
    if (!path) return null
    let op: ToolTarget['op'] = 'edit'
    if (/delete|remove|\brm\b|unlink/.test(n)) op = 'delete'
    else if (/write|create|new/.test(n)) op = 'write'
    else if (/read|view|cat|open/.test(n)) op = 'read'
    return { kind: 'fs', op, target: path.slice(0, TARGET_MAX) }
  }

  return null
}
