import { isNestedShellCommand } from "./activity-item.js";
import { projectPublicToolArguments, sanitizePublicText } from "./public-sanitizer.js";

const MAX_PUBLIC_RESULT_BYTES = 32 * 1024;

export function projectPublicActivityItem(item, config = {}) {
  const nestedShell = isNestedShellCommand(item);
  const text = publicItemText(item, config);
  const error = publicItemError(item, config);
  const exitCode = Number.isInteger(item?.exitCode) ? item.exitCode : undefined;
  return compact({
    kind: nestedShell ? "commandExecution" : item?.type ?? "activity",
    phase: item?.phase,
    summary: sanitizePublicText(activitySummary(item), config),
    status: itemStatus(item, exitCode, error),
    durationMs: item?.durationMs,
    toolCall: toolCallDetails(item),
    text,
    error,
    exitCode
  });
}

export function activitySummary(item) {
  if (!item) return "Working";
  if (isNestedShellCommand(item)) return "PowerShell command";
  if (item.type === "commandExecution") return item.command ?? "Running command";
  if (item.type === "fileChange") return "Updating files";
  if (item.type === "mcpToolCall") return `${item.server ?? "MCP"}: ${item.tool ?? "tool"}`;
  if (item.type === "dynamicToolCall") return item.tool ?? item.name ?? "Tool activity";
  if (item.type === "webSearch") return item.query ? `Searching: ${item.query}` : "Searching the web";
  if (item.type === "reasoning") return "Thinking";
  if (item.type === "plan") return "Updating plan";
  if (item.type === "imageGeneration") return "Generated image";
  if (item.type === "imageView") return "Viewed image";
  if (["collabAgentToolCall", "subAgentActivity"].includes(item.type)) return "Sub-agent activity";
  if (item.type === "sleep") return "Waiting";
  if (item.type === "contextCompaction") return "Compacting context";
  if (item.type === "enteredReviewMode") return "Entered review mode";
  if (item.type === "exitedReviewMode") return "Exited review mode";
  return `Working: ${item.type}`;
}

function itemStatus(item, exitCode, error) {
  if (item?.status === "failed" || error || (Number.isInteger(exitCode) && exitCode !== 0)) return "failed";
  if (["interrupted", "cancelled", "canceled"].includes(item?.status)) return "interrupted";
  return item?.status === "running" || item?.status === "inProgress" ? "running" : "completed";
}

function publicItemText(item, config) {
  if (!item) return undefined;
  if (item.type === "commandExecution") return boundedText(item.aggregatedOutput, config);

  const parts = [];
  for (const content of item.contentItems ?? []) {
    if (content?.type === "inputText" && typeof content.text === "string") parts.push(content.text);
  }
  if (item.type === "mcpToolCall") {
    for (const content of item.result?.content ?? []) {
      if (content?.type === "text" && typeof content.text === "string") parts.push(content.text);
    }
    if (typeof item.result?.content === "string") parts.push(item.result.content);
    if (item.result?.structuredContent != null) {
      try {
        parts.push(JSON.stringify(item.result.structuredContent, null, 2));
      } catch {
        // Ignore values that cannot be safely serialized.
      }
    }
  }
  return boundedText(parts.join("\n"), config);
}

function publicItemError(item, config) {
  const message = typeof item?.error === "string" ? item.error : item?.error?.message;
  return boundedText(message, config);
}

function boundedText(value, config) {
  if (typeof value !== "string" || !value) return undefined;
  const sanitized = sanitizePublicText(value, config);
  const bytes = Buffer.from(sanitized, "utf8");
  if (bytes.length <= MAX_PUBLIC_RESULT_BYTES) return sanitized;
  const prefix = bytes.subarray(0, MAX_PUBLIC_RESULT_BYTES).toString("utf8").replace(/\uFFFD$/, "");
  return `${prefix}\n[output truncated]`;
}

function toolCallDetails(item) {
  if (!["mcpToolCall", "dynamicToolCall", "webSearch"].includes(item?.type)) return undefined;
  return compact({
    server: cleanString(item.server),
    tool: cleanString(item.tool ?? item.name ?? item.query),
    arguments: projectPublicToolArguments(item.arguments ?? item.input ?? item.params)
  });
}

function cleanString(value) {
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
}

function compact(value) {
  return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined && item !== null));
}
