export function visibleActivityText(item) {
  if (item.kind === "reasoning") return item.text ?? "";
  if (item.kind === "agentMessage" && item.phase === "commentary") return item.text ?? "";
  return item.error ?? item.text ?? "";
}

export function activityItemsBetween(items, startAt, endAt) {
  const lower = Number.isFinite(startAt) ? startAt : Number.NEGATIVE_INFINITY;
  return items.filter((item) => {
    const activityAt = Number.isFinite(item.updatedAt)
      ? item.updatedAt
      : Number.isFinite(item.createdAt)
        ? item.createdAt
        : Number.POSITIVE_INFINITY;
    return activityAt >= lower && (endAt == null || activityAt < endAt);
  });
}

export function activityTitle(turn, logCount, t = englishActivityText) {
  if (logCount > 0) return t("activity.log", { count: logCount });
  if (turn.status === "interrupted") return t("activity.interrupted");
  if (turn.status === "failed") return t("activity.failed");
  if (turn.status === "completed") return t("activity.completed");
  return t("activity.started");
}

export function toolCallLabel(item, t = englishActivityText) {
  const summary = String(item.summary ?? "").trim();
  const internalToolName = (value) => /^(?:js|javascript)$/i.test(value);
  if (item.kind === "commandExecution" && summary && !internalToolName(summary)) return summary;
  const tool = item.toolCall?.tool;
  if (internalToolName(tool) || internalToolName(summary)) {
    const label = t("activity.tool") ?? englishActivityText("activity.tool");
    return item.toolCall?.server ? `${item.toolCall.server} ${label}` : label;
  }
  return (tool ?? summary) || t("activity.tool");
}

export function visibleActivityItems(items) {
  return items.filter((item) => {
    if (item.kind === "reasoning") return Boolean(item.text?.trim());
    if (item.kind === "agentMessage") return item.phase === "commentary" && Boolean(item.text?.trim());
    return true;
  });
}

export function groupTechnicalActivity(items) {
  const groups = [];
  let technicalItems = [];

  function flushTechnicalItems() {
    if (technicalItems.length === 0) return;
    groups.push({ type: "technicalGroup", key: technicalItems[0].id, items: technicalItems });
    technicalItems = [];
  }

  for (const item of items) {
    if (isToolCallActivity(item)) {
      flushTechnicalItems();
      groups.push({ type: "item", key: item.id, item });
      continue;
    }
    if (["commandExecution", "fileChange"].includes(item.kind)) {
      technicalItems.push(item);
      continue;
    }

    flushTechnicalItems();
    groups.push({ type: "item", key: item.id, item });
  }

  flushTechnicalItems();
  return groups;
}

function isToolCallActivity(item) {
  return Boolean(item.toolCall) || ["mcpToolCall", "dynamicToolCall", "webSearch"].includes(item.kind);
}

export function technicalGroupLabel(items, t = englishActivityText) {
  const commands = items.filter((item) => item.kind === "commandExecution").length;
  const tools = items.filter((item) => ["mcpToolCall", "dynamicToolCall", "webSearch"].includes(item.kind)).length;
  const files = items
    .filter((item) => item.kind === "fileChange")
    .reduce((count, item) => count + fileChangeCount(item), 0);
  const status = technicalGroupStatus(items, t);

  if (tools > 0) {
    const parts = [];
    if (files > 0) parts.push(t("activity.filesChanged", { count: files }));
    if (commands > 0) parts.push(t("activity.commandsRun", { count: commands }));
    parts.push(t("activity.toolsRun", { count: tools }));
    return `${parts.join(", ")} ${status}`;
  }
  if (files > 0 && commands > 0) return `${t("activity.filesChanged", { count: files })}, ${t("activity.commandsRun", { count: commands })} ${status}`;
  if (files > 0) return `${t("activity.filesChanged", { count: files })} ${status}`;
  return `${t("activity.commandsRun", { count: commands })} ${status}`;
}

export function withFileChangeCount(items) {
  const paths = [...new Set(items.flatMap((item) => item.kind === "fileChange" ? item.paths ?? [] : []))];
  let foundFileChange = false;
  const projected = items.flatMap((item) => {
    if (item.kind !== "fileChange") return [item];
    if (foundFileChange) return [];
    foundFileChange = true;
    return [{
      ...item,
      ...(paths.length > 0 ? { summary: `Updated ${paths.length} files`, paths } : {})
    }];
  });
  return projected;
}

export function activityFiles(items, changedFiles) {
  const paths = [...new Set(items.flatMap((item) => item.kind === "fileChange" ? item.paths ?? [] : []))];
  const expectedCount = items
    .filter((item) => item.kind === "fileChange")
    .reduce((count, item) => count + fileChangeCount(item), 0);
  const activityDiffFiles = items
    .filter((item) => item.kind === "fileChange")
    .flatMap((item) => item.diffs ?? [])
    .filter((file) => typeof file.path === "string");
  if (paths.length > 0 && paths.every(isRedactedPath) && activityDiffFiles.length > 0) {
    return activityDiffFiles.map((file) => ({
      path: file.path,
      status: "modified",
      ...(file.diff ? { diff: file.diff } : {})
    }));
  }
  if (paths.length > 0 && paths.every(isRedactedPath) && changedFiles.length === expectedCount) return changedFiles;
  const activityDiffs = new Map(activityDiffFiles.map((file) => [file.path, file]));
  const currentFiles = new Map(changedFiles.map((file) => [file.path, file]));
  return paths.map((path) => ({
    ...(currentFiles.get(path) ?? { path, status: "modified" }),
    ...(activityDiffs.get(path)?.diff ? { diff: activityDiffs.get(path).diff } : {})
  }));
}

function isRedactedPath(path) {
  return path === "[private-path]" || path === "[redacted]";
}

function fileChangeCount(item) {
  const match = String(item.summary ?? "").match(/(?:updated|changed)\s+(\d+)\s+file/i);
  return match ? Number(match[1]) : 1;
}

function technicalGroupStatus(items, t) {
  if (items.some((item) => item.status === "running")) return t("activity.running");
  const failed = items.filter((item) => item.status === "failed").length;
  if (failed > 0) return failed === items.length ? t("activity.failed") : `· ${failed} ${t("activity.failed")}`;
  const interrupted = items.filter((item) => item.status === "interrupted").length;
  if (interrupted > 0) return interrupted === items.length ? t("activity.interrupted") : `· ${interrupted} ${t("activity.interrupted")}`;
  return t("activity.completed");
}

function englishActivityText(key, values = {}) {
  const messages = {
    "activity.log": `Work log (${values.count})`,
    "activity.interrupted": "Stopped",
    "activity.failed": "Failed",
    "activity.completed": "Completed",
    "activity.started": "Starting work",
    "activity.running": "Working",
    "activity.command": "Command",
    "activity.tool": "Tool",
    "activity.filesChanged": `${values.count} files changed`,
    "activity.commandsRun": `${values.count} commands run`,
    "activity.toolsRun": `${values.count} tools run`
  };
  return messages[key] ?? key;
}

export function groupConsecutiveCommands(items) {
  const groups = [];
  let commands = [];
  let commandKey = "";

  function flushCommands() {
    if (commands.length === 1) {
      groups.push({ type: "item", key: commands[0].id, item: commands[0] });
    } else if (commands.length > 1) {
      groups.push({
        type: "commandGroup",
        key: commands[0].id,
        summary: commands[0].summary,
        items: commands
      });
    }
    commands = [];
    commandKey = "";
  }

  for (const item of items) {
    const itemKey = commandSimilarityKey(item);
    const matches = item.kind === "commandExecution"
      && commands.length > 0
      && itemKey
      && itemKey === commandKey;
    if (matches) {
      commands.push(item);
      continue;
    }

    flushCommands();
    if (item.kind === "commandExecution") {
      commands.push(item);
      commandKey = itemKey;
    }
    else groups.push({ type: "item", key: item.id, item });
  }

  flushCommands();
  return groups;
}

function commandSimilarityKey(item) {
  if (item.kind !== "commandExecution") return "";
  const summary = normalizeCommandSummary(item.summary);
  if (!summary) return "";
  const executable = summary.match(/[a-z]:\\(?:[^"'<>|:*?\r\n]+\\)*[^"'<>|:*?\r\n]+\.exe/i);
  return executable ? `exe:${executable[0].toLowerCase()}` : `cmd:${summary}`;
}

function normalizeCommandSummary(summary) {
  return String(summary ?? "")
    .trim()
    .replace(/^>_\s*/, "")
    .replace(/^["']|["']$/g, "")
    .replace(/\\\\/g, "\\")
    .replace(/\s+/g, " ");
}

export function activityLogCount(turn) {
  const visibleItems = visibleActivityItems(turn.activity.items);
  return turn.activity.plan.length
    + visibleItems.length
    + turn.approvals.filter((approval) => approval.status !== "pending").length
    + (turn.error ? 1 : 0);
}
