import { spawn } from "child_process";
import { EventEmitter } from "events";
import { realpathSync, statSync } from "fs";
import { basename, dirname, join, normalize, resolve } from "path";

function isPlainObject(value) {
  return value && typeof value === "object" && !Array.isArray(value);
}

const CLAUDE_ACCEPT_EDITS_TOOLS = new Set(["Edit", "MultiEdit", "NotebookEdit", "Write"]);

function normalizeClaudePermissionModeValue(value, fallback = null) {
  const normalized = typeof value === "string" ? value.trim() : "";
  return normalized || fallback;
}

function normalizeClaudeCwdValue(value, fallback = null) {
  const normalized = typeof value === "string" ? value.trim() : "";
  return normalized || fallback;
}

function getClaudeInitToolsList(msg) {
  return Array.isArray(msg?.tools) ? msg.tools : [];
}

function buildClaudeDefaultAllowResponse() {
  return { behavior: "allow", updatedInput: {} };
}

function isClaudePermissionSuggestion(value) {
  return isPlainObject(value) && typeof value.type === "string" && value.type.trim();
}

function canonicalizeClaudeGrantDirectory(path) {
  if (typeof path !== "string" || !path.trim()) {
    throw new Error("Claude addDirectories suggestion requires a non-empty directory path");
  }
  const candidate = resolve(normalize(path.trim()));
  const stats = statSync(candidate, { throwIfNoEntry: false });
  if (!stats) {
    throw new Error(`Claude addDirectories suggestion path does not exist: ${candidate}`);
  }
  if (!stats.isDirectory()) {
    throw new Error(`Claude addDirectories suggestion path is not a directory: ${candidate}`);
  }
  return realpathSync(candidate);
}

function canonicalizeClaudeTargetPath(path) {
  if (typeof path !== "string" || !path.trim()) {
    throw new Error("Claude tool path matching requires a non-empty path");
  }
  const candidate = resolve(normalize(path.trim()));
  const stats = statSync(candidate, { throwIfNoEntry: false });
  if (stats) {
    return realpathSync(candidate);
  }
  const parent = dirname(candidate);
  const parentStats = statSync(parent, { throwIfNoEntry: false });
  if (!parentStats || !parentStats.isDirectory()) {
    throw new Error(`Claude tool path parent does not exist: ${parent}`);
  }
  return join(realpathSync(parent), basename(candidate));
}

function normalizeGrantedDirectoryEntries(existingEntries = [], nextPaths = []) {
  const merged = [];
  const seen = new Set();
  for (const entry of [...existingEntries, ...nextPaths.map((path) => ({ raw: path, path }))]) {
    const canonicalPath = typeof entry?.path === "string" && entry.path ? entry.path : null;
    if (!canonicalPath || seen.has(canonicalPath)) continue;
    seen.add(canonicalPath);
    merged.push({
      raw: typeof entry?.raw === "string" && entry.raw ? entry.raw : canonicalPath,
      path: canonicalPath,
    });
  }
  return merged;
}

function pathIsWithinAllowedDirectory(candidatePath, allowedDirectory) {
  return candidatePath === allowedDirectory || candidatePath.startsWith(`${allowedDirectory}/`);
}

const AGENT_FILE_PATTERNS = [
  ".claude/commands/",
  ".claude/settings.json",
  ".claude/skills/",
  ".claude/CLAUDE.md",
];
const AGENT_ROOT_FILES = ["CLAUDE.md", "AGENTS.md"];

function isAgentFilePath(candidatePath, cwd) {
  if (!candidatePath || !cwd) return false;
  const resolved = resolve(normalize(candidatePath));
  const normalizedCwd = resolve(normalize(cwd));
  const relative = resolved.startsWith(normalizedCwd + "/")
    ? resolved.slice(normalizedCwd.length + 1)
    : null;
  if (!relative) return false;
  for (const pattern of AGENT_FILE_PATTERNS) {
    if (relative.startsWith(pattern) || relative === pattern.replace(/\/$/, "")) return true;
  }
  for (const rootFile of AGENT_ROOT_FILES) {
    if (relative === rootFile) return true;
  }
  return false;
}

function hasAgentFilePaths(candidatePaths, cwd) {
  return candidatePaths.some((p) => isAgentFilePath(p, cwd));
}

function extractClaudeToolPaths(value, key = "") {
  if (typeof value === "string") {
    const normalizedKey = key.toLowerCase();
    if (
      normalizedKey.includes("path")
      || normalizedKey === "cwd"
      || normalizedKey.includes("directory")
    ) {
      return [value];
    }
    return [];
  }
  if (Array.isArray(value)) {
    return value.flatMap((entry) => extractClaudeToolPaths(entry, key));
  }
  if (!isPlainObject(value)) return [];
  return Object.entries(value).flatMap(([entryKey, entryValue]) => extractClaudeToolPaths(entryValue, entryKey));
}

/**
 * Claude Code adapter for Oysterun's current interactive session runtime.
 * This isolates Claude-specific process spawning and stream-json protocol details
 * behind a dedicated adapter so the manager can become provider-neutral later.
 */
export class ClaudeCodeAdapter {
  constructor(options = {}) {
    this.command = options.command || "claude";
    this.getConfiguredCommandFn = typeof options.getConfiguredCommand === "function"
      ? options.getConfiguredCommand
      : null;
    this.spawnFn = options.spawnFn || spawn;
    this.providerId = "claude";
    this.capabilities = {
      interactiveSession: true,
      resume: true,
      permissionResponses: true,
      historyImport: false,
      interrupt: true,
      workspacePolicy: true,
    };
  }

  getConfiguredCommand() {
    if (!this.getConfiguredCommandFn) return null;
    const value = this.getConfiguredCommandFn();
    return typeof value === "string" && value.trim() ? value.trim() : null;
  }

  resolveCommand() {
    return this.getConfiguredCommand() || this.command;
  }

  /**
   * Start a Claude Code interactive session.
   * @param {object} opts
   * @param {string} opts.sessionId
   * @param {string} opts.cwd
   * @param {string} opts.agentId
   * @param {string} [opts.resumeSessionId]
   * @param {boolean} [opts.fork]
   * @param {string} [opts.model]
   * @param {string} [opts.reasoningEffort]
   * @param {string} [opts.permissionMode]
   * @param {boolean} [opts.chromeEnabled]
   * @param {boolean} [opts.allowDangerouslySkipPermissions]
   * @returns {ClaudeCodeSession}
   */
  startSession({
    sessionId,
    cwd,
    agentId,
    resumeSessionId,
    fork = false,
    model,
    reasoningEffort,
    permissionMode,
    chromeEnabled,
    allowDangerouslySkipPermissions,
    runtimeEnv,
  }) {
    return new ClaudeCodeSession({
      id: sessionId,
      command: this.resolveCommand(),
      spawnFn: this.spawnFn,
      cwd,
      agentId,
      resumeSessionId,
      fork,
      model,
      reasoningEffort,
      permissionMode,
      chromeEnabled,
      allowDangerouslySkipPermissions,
      runtimeEnv,
    });
  }

  sendMessage(session, payload) {
    session.send(payload.text);
  }

  respondToControl(session, payload) {
    session.respondToControl(payload);
  }

  stopSession(session) {
    session.stop();
  }

  interruptSession(session) {
    session.interrupt();
  }

  killSession(session) {
    session.kill();
  }

  supportsInteractiveSession() {
    return true;
  }

  supportsResume() {
    return true;
  }

  supportsPermissionResponses() {
    return true;
  }
}

/**
 * A single Claude Code session backed by a child process.
 */
export class ClaudeCodeSession extends EventEmitter {
  constructor(options) {
    super();
    this.id = options.id;
    this.command = options.command;
    this.spawnFn = options.spawnFn;
    this.providerSessionId = null;
    this.providerResumeId = null;
    this.proc = null;
    this.cwd = options.cwd;
    this.agentId = options.agentId;
    this.model = options.model || null;
    this.reasoningEffort = options.reasoningEffort || null;
    this.permissionMode = options.permissionMode || null;
    this.chromeEnabled = options.chromeEnabled === true;
    this.allowDangerouslySkipPermissions = options.allowDangerouslySkipPermissions === true;
    this.runtimeEnv =
      options.runtimeEnv &&
      typeof options.runtimeEnv === "object" &&
      !Array.isArray(options.runtimeEnv)
        ? { ...options.runtimeEnv }
        : {};
    this.toolsCount = null;
    this.provider = "claude";
    this.capabilities = {
      interactiveSession: true,
      resume: true,
      permissionResponses: true,
      historyImport: false,
      interrupt: true,
      workspacePolicy: true,
    };
    this.alive = false;
    this.interrupting = false;
    this.resuming = false;
    this.expectedExitReason = null;
    this.hasSentUserMessage = false;
    this._interruptAfterReady = false;
    this._pendingMessages = [];
    this._buffer = "";
    this._ready = false;
    this.attachProcess(this.spawnProcess({
      resumeSessionId: options.resumeSessionId,
      fork: options.fork === true,
    }));
  }

  buildArgs({ resumeSessionId, fork = false }) {
    const args = [
      "-p",
      "--output-format", "stream-json",
      "--input-format", "stream-json",
      "--permission-prompt-tool", "stdio",
    ];
    args.push("--verbose");
    if (resumeSessionId) {
      args.push("--resume", resumeSessionId);
      if (fork === true) {
        args.push("--fork-session");
      }
    }
    if (this.model) {
      args.push("--model", this.model);
    }
    if (this.reasoningEffort && this.reasoningEffort !== "auto") {
      args.push("--effort", this.reasoningEffort);
    }
    if (this.permissionMode) {
      args.push("--permission-mode", this.permissionMode);
    }
    if (this.chromeEnabled) {
      args.push("--chrome");
    }
    return args;
  }

  spawnProcess({ resumeSessionId, fork = false }) {
    // Avoid Claude Code detecting this as a nested session when Oysterun is
    // itself running inside Claude Code.
    const baseEnv = { ...process.env };
    delete baseEnv.CLAUDECODE;
    const env = { ...baseEnv, ...this.runtimeEnv };
    return this.spawnFn(this.command, this.buildArgs({ resumeSessionId, fork }), {
      cwd: this.cwd,
      stdio: ["pipe", "pipe", "pipe"],
      env,
    });
  }

  attachProcess(proc) {
    this.proc = proc;
    this.alive = true;
    this._buffer = "";
    this._ready = false;
    this._pendingControls = new Map();
    this._permissionSuggestionState = {
      acceptEdits: false,
      addedDirectories: [],
    };

    proc.stdout.on("data", (chunk) => {
      this._buffer += chunk.toString();
      const lines = this._buffer.split("\n");
      this._buffer = lines.pop() ?? "";
      for (const line of lines) {
        if (!line.trim()) continue;
        try {
          const msg = JSON.parse(line);
          if (msg.type === "system" && msg.subtype === "init") {
            this._ready = true;
            this.resuming = false;
            this.model = msg.model || this.model;
            this.permissionMode = normalizeClaudePermissionModeValue(msg.permissionMode, this.permissionMode);
            this.cwd = normalizeClaudeCwdValue(msg.cwd, this.cwd);
            this.toolsCount = getClaudeInitToolsList(msg).length;
            if (msg.session_id) {
              this.providerSessionId = msg.session_id;
              this.providerResumeId = msg.session_id;
            }
            if (this._interruptAfterReady) {
              this._interruptAfterReady = false;
              this.proc.kill("SIGINT");
            } else {
              this.drainPendingMessages();
            }
          }
          if (msg.type === "system" && msg.subtype === "status") {
            this.permissionMode = normalizeClaudePermissionModeValue(msg.permissionMode, this.permissionMode);
          }
          for (const event of this.normalizeMessage(msg)) {
            this.emit("event", event);
          }
        } catch {
          this.emit("raw", line);
        }
      }
    });

    proc.stderr.on("data", (chunk) => {
      this.emit("event", {
        type: "stderr",
        provider: this.provider,
        text: chunk.toString(),
      });
    });

    proc.on("exit", (code, signal) => {
      this.alive = false;
      this.handleProcessExit(code, signal);
    });

    proc.on("error", (err) => {
      this.alive = false;
      this.emit("error", err);
    });
  }

  send(text) {
    if (!this.alive && !this.interrupting && !this.resuming) {
      throw new Error(`Session ${this.id} is not alive`);
    }
    if (!this.canSendImmediately()) {
      this._pendingMessages.push(text);
      return;
    }
    this.writeUserMessage(text);
  }

  canSendImmediately() {
    return Boolean(
      this.alive
      && !this.interrupting
      && this.proc
      && this.proc.stdin
      && this.proc.stdin.writable
    );
  }

  writeUserMessage(text) {
    this.hasSentUserMessage = true;
    const msg = {
      type: "user",
      message: { role: "user", content: text },
      parent_tool_use_id: null,
      session_id: this.providerSessionId || this.id,
    };
    this.proc.stdin.write(JSON.stringify(msg) + "\n");
  }

  drainPendingMessages() {
    if (!this.canSendImmediately()) return;
    while (this._pendingMessages.length > 0) {
      this.writeUserMessage(this._pendingMessages.shift());
    }
  }

  respondToControl(payload) {
    if (!this.alive) throw new Error(`Session ${this.id} is not alive`);
    const requestId = payload?.requestId || payload?.request_id;
    if (typeof requestId !== "string" || !requestId) {
      throw new Error("Claude control response is missing requestId");
    }
    const pending = this._pendingControls.get(requestId);
    this._pendingControls.delete(requestId);
    let responsePayload = payload?.response;
    if (!isPlainObject(responsePayload)) {
      if (typeof payload.allow !== "boolean") {
        throw new Error("Claude control response requires boolean allow or response payload");
      }
      responsePayload = payload.allow === true
        ? buildClaudeDefaultAllowResponse()
        : { behavior: "deny", message: "Denied by owner" };
    }
    if (payload.allow === true && payload?.grantSuggestion !== undefined) {
      this.applyPermissionSuggestionGrant(payload.grantSuggestion, pending?.payload || null);
    }
    const msg = {
      type: "control_response",
      response: {
        subtype: "success",
        request_id: requestId,
        response: responsePayload,
      },
    };
    this.proc.stdin.write(JSON.stringify(msg) + "\n");
  }

  getPendingControlRequests() {
    return [...this._pendingControls.entries()].map(([requestId, pending]) => ({
      provider: this.provider,
      request_id: requestId,
      subtype: pending.subtype,
      payload: pending.payload,
    }));
  }

  applyPermissionSuggestionGrant(rawSuggestion, payload) {
    if (!isClaudePermissionSuggestion(rawSuggestion)) {
      throw new Error("Claude permission grant requires a structured suggestion object");
    }
    const destination = typeof rawSuggestion.destination === "string" ? rawSuggestion.destination.trim() : "";
    if (destination !== "session") {
      throw new Error(`Unsupported Claude permission suggestion destination: ${destination || "missing"}`);
    }

    if (rawSuggestion.type === "setMode") {
      const mode = typeof rawSuggestion.mode === "string" ? rawSuggestion.mode.trim() : "";
      if (mode !== "acceptEdits") {
        throw new Error(`Unsupported Claude setMode suggestion: ${mode || "missing"}`);
      }
      this._permissionSuggestionState.acceptEdits = true;
      this.emit("event", {
        type: "session.notice",
        provider: this.provider,
        subtype: "control.permission_grant_applied",
        payload: {
          request_payload: payload,
          grant: {
            type: "setMode",
            mode,
            destination,
          },
        },
      });
      return;
    }

    if (rawSuggestion.type === "addDirectories") {
      if (!Array.isArray(rawSuggestion.directories) || rawSuggestion.directories.length === 0) {
        throw new Error("Claude addDirectories suggestion requires at least one directory");
      }
      const canonicalDirectories = rawSuggestion.directories.map((entry) => canonicalizeClaudeGrantDirectory(entry));
      this._permissionSuggestionState.addedDirectories = normalizeGrantedDirectoryEntries(
        this._permissionSuggestionState.addedDirectories,
        canonicalDirectories,
      );
      if (this.workspacePolicy) {
        this.workspacePolicy = {
          ...this.workspacePolicy,
          allowedPaths: normalizeGrantedDirectoryEntries(
            Array.isArray(this.workspacePolicy.allowedPaths) ? this.workspacePolicy.allowedPaths : [],
            canonicalDirectories,
          ),
        };
      }
      this.emit("event", {
        type: "session.notice",
        provider: this.provider,
        subtype: "control.permission_grant_applied",
        payload: {
          request_payload: payload,
          grant: {
            type: "addDirectories",
            directories: canonicalDirectories,
            destination,
          },
        },
      });
      return;
    }

    throw new Error(`Unsupported Claude permission suggestion type: ${rawSuggestion.type}`);
  }

  shouldAutoApproveControlRequest(requestPayload) {
    if (!isPlainObject(requestPayload) || requestPayload.subtype !== "can_use_tool") {
      return null;
    }
    const toolName = typeof requestPayload.tool_name === "string" ? requestPayload.tool_name.trim() : "";

    // Agent file gate: always escalate to user, regardless of permission mode.
    const candidatePaths = extractClaudeToolPaths(requestPayload.input);
    if (candidatePaths.length > 0 && hasAgentFilePaths(candidatePaths, this.cwd)) {
      return null;
    }

    // Bypass mode: auto-approve everything that is not an agent file.
    if (this.permissionMode === "bypassPermissions") {
      return {
        reason: "bypassPermissions",
        response: buildClaudeDefaultAllowResponse(),
      };
    }

    // acceptEdits: auto-approve edit tools.
    if (this._permissionSuggestionState.acceptEdits === true && CLAUDE_ACCEPT_EDITS_TOOLS.has(toolName)) {
      return {
        reason: "acceptEdits",
        response: buildClaudeDefaultAllowResponse(),
      };
    }

    // addDirectories: auto-approve if all paths are within granted directories.
    if (this._permissionSuggestionState.addedDirectories.length === 0) {
      return null;
    }
    if (!candidatePaths.length) return null;
    const canonicalPaths = candidatePaths.map((entry) => canonicalizeClaudeTargetPath(entry));
    const allowedDirectories = this._permissionSuggestionState.addedDirectories.map((entry) => entry.path);
    const allAllowed = canonicalPaths.every((candidatePath) => (
      allowedDirectories.some((allowedDirectory) => pathIsWithinAllowedDirectory(candidatePath, allowedDirectory))
    ));
    if (!allAllowed) return null;

    return {
      reason: "addDirectories",
      response: buildClaudeDefaultAllowResponse(),
    };
  }

  stop() {
    this.expectedExitReason = "stop";
    this.interrupting = false;
    this.resuming = false;
    if (this.proc.stdin.writable) {
      this.proc.stdin.end();
    }
  }

  interrupt() {
    if (!this.alive) throw new Error(`Session ${this.id} is not alive`);
    if (this.interrupting || this.resuming) return;
    this.expectedExitReason = "interrupt";
    this.interrupting = true;
    this.emit("event", {
      type: "session.notice",
      provider: this.provider,
      subtype: "interrupt.requested",
    });
    if (!this.providerResumeId && !this._ready) {
      if (!this.hasSentUserMessage) {
        this.interrupting = false;
        this.expectedExitReason = null;
        return;
      }
      this._interruptAfterReady = true;
      return;
    }
    this.proc.kill("SIGINT");
  }

  kill() {
    this.expectedExitReason = "kill";
    this.interrupting = false;
    this.resuming = false;
    this._interruptAfterReady = false;
    this.proc.kill("SIGTERM");
    setTimeout(() => {
      if (this.alive) this.proc.kill("SIGKILL");
    }, 5000);
  }

  handleProcessExit(code, signal) {
    const shouldResume =
      this.expectedExitReason === "interrupt"
      && typeof this.providerResumeId === "string"
      && this.providerResumeId.trim().length > 0;
    this.expectedExitReason = null;

    if (shouldResume) {
      this.interrupting = false;
      this.resuming = true;
      this.emit("event", {
        type: "turn.completed",
        provider: this.provider,
        turns: null,
        duration_ms: null,
        usage: null,
        result: null,
        status: "interrupted",
      });
      this.emit("event", {
        type: "session.notice",
        provider: this.provider,
        subtype: "interrupt.resuming",
      });
      try {
        this.attachProcess(this.spawnProcess({
          resumeSessionId: this.providerResumeId,
          fork: false,
        }));
        this.emit("event", {
          type: "session.notice",
          provider: this.provider,
          subtype: "interrupt.respawned",
        });
        this.drainPendingMessages();
        return;
      } catch (err) {
        this.resuming = false;
        this.emit("error", err);
      }
    }

    this.interrupting = false;
    this.resuming = false;
    this._interruptAfterReady = false;
    this.emit("exit", code, signal);
  }

  normalizeMessage(msg) {
    if (msg.type === "control_request") {
      const requestId = typeof msg.request_id === "string" && msg.request_id ? msg.request_id : null;
      const subtype = msg.request?.subtype || null;
      const payload = msg.request || null;
      const autoApproval = requestId ? this.shouldAutoApproveControlRequest(payload) : null;
      if (requestId && autoApproval) {
        const controlResponse = {
          type: "control_response",
          response: {
            subtype: "success",
            request_id: requestId,
            response: autoApproval.response,
          },
        };
        this.proc.stdin.write(JSON.stringify(controlResponse) + "\n");
        return [{
          type: "session.notice",
          provider: this.provider,
          subtype: "control.auto_allowed",
          payload: {
            request_id: requestId,
            reason: autoApproval.reason,
            tool_name: payload?.tool_name || null,
          },
        }];
      }
      if (requestId) {
        this._pendingControls.set(requestId, {
          subtype,
          payload,
        });
      }
      const candidatePaths = payload ? extractClaudeToolPaths(payload.input) : [];
      const sensitive = candidatePaths.length > 0 && hasAgentFilePaths(candidatePaths, this.cwd);
      return [{
        type: "control.request",
        provider: this.provider,
        request_id: requestId,
        subtype,
        payload,
        sensitive,
      }];
    }

    if (msg.type === "system" && msg.subtype === "init") {
      const providerResumeId = typeof msg.session_id === "string" && msg.session_id.trim()
        ? msg.session_id.trim()
        : null;
      if (providerResumeId) {
        this.providerSessionId = providerResumeId;
        this.providerResumeId = providerResumeId;
      }
      const nextPermissionMode = normalizeClaudePermissionModeValue(msg.permissionMode, this.permissionMode);
      const nextCwd = normalizeClaudeCwdValue(msg.cwd, this.cwd);
      const tools = getClaudeInitToolsList(msg);
      this.model = msg.model || this.model;
      this.permissionMode = nextPermissionMode;
      this.cwd = nextCwd;
      this.toolsCount = tools.length;
      return [{
        type: "session.ready",
        provider: this.provider,
        session_id: providerResumeId || this.id,
        provider_session_id: providerResumeId,
        provider_resume_id: providerResumeId,
        model: msg.model || null,
        tools,
        toolsCount: tools.length,
        permissionMode: nextPermissionMode,
        cwd: nextCwd,
      }];
    }

    if (msg.type === "system") {
      if (msg.subtype === "status") {
        const nextPermissionMode = normalizeClaudePermissionModeValue(msg.permissionMode, this.permissionMode);
        if (nextPermissionMode) {
          this.permissionMode = nextPermissionMode;
          return [{
            type: "session.notice",
            provider: this.provider,
            subtype: "permission_mode.updated",
            payload: {
              permissionMode: nextPermissionMode,
              status: msg.status ?? null,
            },
          }];
        }
        return [{
          type: "session.notice",
          provider: this.provider,
          subtype: "status",
          payload: {
            status: msg.status ?? null,
          },
        }];
      }
      return [{
        type: "session.notice",
        provider: this.provider,
        subtype: msg.subtype || null,
      }];
    }

    if (msg.type === "assistant") {
      const events = [];

      if (Array.isArray(msg.message?.content)) {
        for (const block of msg.message.content) {
          if (block.type === "thinking" && typeof block.thinking === "string") {
            const thinkingText = block.thinking.trim();
            if (!thinkingText) {
              continue;
            }
            events.push({
              type: "message.thinking",
              provider: this.provider,
              text: thinkingText,
              signature: block.signature || null,
              redacted: false,
            });
            continue;
          }

          if (block.type === "redacted_thinking") {
            continue;
          }

          if (block.type === "text" && block.text) {
            events.push({
              type: "message.assistant",
              provider: this.provider,
              text: block.text,
              delta: false,
            });
            continue;
          }

          if (block.type === "tool_use") {
            events.push({
              type: "tool.call",
              provider: this.provider,
              call_id: block.id || null,
              name: block.name || null,
              input: block.input ?? null,
            });
            continue;
          }

          if (block.type === "tool_result") {
            events.push({
              type: "tool.result",
              provider: this.provider,
              call_id: block.tool_use_id || null,
              content: block.content ?? null,
              is_error: block.is_error === true,
            });
          }
        }
      }

      if (typeof msg.delta === "string" && msg.delta) {
        events.push({
          type: "message.assistant",
          provider: this.provider,
          text: msg.delta,
          delta: true,
        });
      }

      return events;
    }

    if (msg.type === "user" && Array.isArray(msg.message?.content)) {
      const events = [];
      for (const block of msg.message.content) {
        if (block.type === "tool_result") {
          events.push({
            type: "tool.result",
            provider: this.provider,
            call_id: block.tool_use_id || null,
            content: block.content ?? null,
            is_error: block.is_error === true,
          });
        }
      }
      return events;
    }

    if (msg.type === "result") {
      return [{
        type: "turn.completed",
        provider: this.provider,
        turns: msg.num_turns ?? msg.turns ?? null,
        duration_ms: msg.duration_ms ?? null,
        usage: msg.usage ?? msg.cost ?? null,
        result: msg.result ?? null,
      }];
    }

    if (msg.type === "stream_event" && msg.event?.delta?.type === "text_delta") {
      return [{
        type: "message.assistant",
        provider: this.provider,
        text: msg.event.delta.text || "",
        delta: true,
      }];
    }

    return [];
  }
}
