import { query, type Options } from "@anthropic-ai/claude-agent-sdk";
import { brandSafeError, type ServerMessage } from "@agent-client/shared";
import { createRequire } from "node:module";
import { existsSync } from "node:fs";
import path from "node:path";

export interface RunAgentParams {
  /** Caller-supplied turn id, echoed on every emitted ServerMessage. */
  id: string;
  prompt: string;
  /** Working directory used for tool execution. Defaults to process.cwd(). */
  cwd?: string;
  /** Outside abort handle. Aborting stops the SDK's agent loop. */
  abortController?: AbortController;
  /** Where to send mapped messages. */
  onMessage: (msg: ServerMessage) => void;
}

const SETTING_SOURCES = (process.env.SETTING_SOURCES?.split(",") ?? [
  "project",
]) as Options["settingSources"];

const MODEL = process.env.MODEL ?? "claude-sonnet-4-6";

/**
 * Resolve the Claude Code native binary path.
 *
 * The SDK's auto-detection picks the wrong libc variant on Linux glibc
 * systems (it tries the musl variant first, which fails to exec because the
 * musl dynamic loader isn't present on glibc — see
 * .learnings/ERRORS/ERR-20260510-002). We override by walking the installed
 * @anthropic-ai sibling packages and preferring glibc → musl on Linux.
 */
const PLATFORM_BINS: Record<string, string[]> = {
  "linux-x64": [
    "claude-agent-sdk-linux-x64/claude",
    "claude-agent-sdk-linux-x64-musl/claude",
  ],
  "linux-arm64": [
    "claude-agent-sdk-linux-arm64/claude",
    "claude-agent-sdk-linux-arm64-musl/claude",
  ],
  "darwin-x64": ["claude-agent-sdk-darwin-x64/claude"],
  "darwin-arm64": ["claude-agent-sdk-darwin-arm64/claude"],
  "win32-x64": ["claude-agent-sdk-win32-x64/claude.exe"],
};

const resolvedBinary: string | undefined = (() => {
  const explicit = process.env.CLAUDE_CODE_EXECUTABLE;
  if (explicit && existsSync(explicit)) return explicit;

  let sdkDir: string;
  try {
    const requireFn = createRequire(import.meta.url);
    sdkDir = path.dirname(requireFn.resolve("@anthropic-ai/claude-agent-sdk"));
  } catch {
    return undefined;
  }
  const parent = path.dirname(sdkDir); // node_modules/@anthropic-ai

  const key = `${process.platform}-${process.arch}`;
  const candidates = (PLATFORM_BINS[key] ?? []).map((rel) =>
    path.join(parent, rel),
  );
  return candidates.find((p) => existsSync(p));
})();

// Anthropic Beta message content shape — narrower than the SDK's `unknown`.
interface AssistantBlock {
  type: "text" | "tool_use" | string;
  text?: string;
  id?: string;
  name?: string;
  input?: unknown;
}
interface UserToolResultBlock {
  type: "tool_result" | string;
  tool_use_id?: string;
  content?: unknown;
  is_error?: boolean;
}

/**
 * Drive Agent SDK for a single turn. Emits ServerMessage-shaped events via
 * `onMessage`. Resolves when the turn ends (result or error).
 */
export async function runAgent(p: RunAgentParams): Promise<void> {
  const { id, prompt, cwd = process.cwd(), abortController, onMessage } = p;

  let sessionEmitted = false;

  const q = query({
    prompt,
    options: {
      cwd,
      abortController,
      model: MODEL,
      settingSources: SETTING_SOURCES,
      tools: { type: "preset", preset: "claude_code" },
      executable: "node",
      // v0.1 explicitly skips permission prompts — there's no UI to handle
      // them. v1 will wire canUseTool to a WS round-trip + browser dialog.
      permissionMode: "bypassPermissions",
      ...(resolvedBinary ? { pathToClaudeCodeExecutable: resolvedBinary } : {}),
    },
  });

  try {
    for await (const sdkMsg of q) {
      if (!sessionEmitted && "session_id" in sdkMsg && sdkMsg.session_id) {
        onMessage({
          type: "turn_started",
          id,
          session_id: sdkMsg.session_id,
        });
        sessionEmitted = true;
      }

      switch (sdkMsg.type) {
        case "assistant": {
          const content = sdkMsg.message.content;
          if (!Array.isArray(content)) break;
          for (const block of content as AssistantBlock[]) {
            if (block.type === "text" && typeof block.text === "string") {
              onMessage({ type: "assistant_text", id, text: block.text });
            } else if (
              block.type === "tool_use" &&
              typeof block.id === "string" &&
              typeof block.name === "string"
            ) {
              onMessage({
                type: "tool_use",
                id,
                tool_use_id: block.id,
                tool: block.name,
                input: block.input,
              });
            }
          }
          break;
        }

        case "user": {
          const content = sdkMsg.message.content;
          if (!Array.isArray(content)) break;
          for (const block of content as UserToolResultBlock[]) {
            if (block.type !== "tool_result" || !block.tool_use_id) continue;
            const output =
              typeof block.content === "string"
                ? block.content
                : JSON.stringify(block.content);
            onMessage({
              type: "tool_result",
              id,
              tool_use_id: block.tool_use_id,
              output,
              is_error: block.is_error ?? false,
            });
          }
          break;
        }

        case "result":
          if (sdkMsg.subtype === "success") {
            onMessage({
              type: "turn_done",
              id,
              success: !sdkMsg.is_error,
              summary: sdkMsg.result,
              usage: {
                input_tokens: sdkMsg.usage?.input_tokens,
                output_tokens: sdkMsg.usage?.output_tokens,
                total_cost_usd: sdkMsg.total_cost_usd,
              },
            });
          } else {
            onMessage({
              type: "error",
              id,
              message: brandSafeError(`turn failed: ${sdkMsg.subtype}`),
            });
          }
          break;

        // Other SDK messages (system, partial, hooks, status...) are not
        // forwarded in v0.1.
        default:
          break;
      }
    }
  } catch (err) {
    onMessage({ type: "error", id, message: brandSafeError(err) });
  }
}
