import { API_BASE } from "./app/api.js";

export function projectConversation(events) {
  const turns = [];
  const byTurnId = new Map();
  let optimisticallyInterruptedTurn;

  for (const event of events) {
    const data = event.data ?? {};
    const eventTime = Date.parse(event.createdAt) || 0;

    if (event.event === "message.accepted") {
      const userText = visibleUserText(data.text ?? "", data.attachmentCount);
      const userAttachments = safeBrowserAttachments(data.attachments, data.sessionId);
      if (userText == null) continue;
      if (data.steering && data.turnId && byTurnId.has(data.turnId)) {
        if (data.queuedMessageId) {
          const queuedIndex = turns.findIndex((turn) => turn.queuedMessageId === data.queuedMessageId);
          if (queuedIndex >= 0) turns.splice(queuedIndex, 1);
        }
        addUserMessage(byTurnId.get(data.turnId), userText, eventTime, userAttachments);
        continue;
      }
      if (data.queued && data.turnId && data.queuedMessageId) {
        const queuedTurn = turns.find((turn) => turn.queuedMessageId === data.queuedMessageId && turn.status === "queued");
        if (queuedTurn) {
          bindTurnId(queuedTurn, data.turnId, byTurnId);
          setTurnPhase(queuedTurn, "running");
          queuedTurn.startedAt = eventTime;
          continue;
        }
      }
      const existingTurn = data.turnId ? byTurnId.get(data.turnId) : undefined;
      if (existingTurn) {
        addUserMessage(existingTurn, userText, eventTime, userAttachments, { dedupeText: true });
        continue;
      }
      const turn = createTurn(event.id, userText, eventTime, userAttachments);
      if (data.optimistic || data.optimisticAccepted) turn.optimistic = true;
      if (data.optimistic && !data.optimisticAccepted) turn.startedAt = undefined;
      if (data.optimisticAccepted) {
        const acceptedAt = Date.parse(data.acceptedAt);
        if (Number.isFinite(acceptedAt)) turn.startedAt = acceptedAt;
      }
      setTurnPhase(turn, data.optimistic && !data.optimisticAccepted ? "sending" : "thinking");
      if (data.queued) turn.status = "queued";
      if (data.queuedMessageId) turn.queuedMessageId = data.queuedMessageId;
      turns.push(turn);
      bindTurnId(turn, data.turnId, byTurnId);
      continue;
    }

    if (event.event === "session.status") {
      if (data.status === "running") {
        const turn = optimisticallyInterruptedTurn;
        if (turn) {
          const activeTurn = findLatestActiveTurn(turns);
          if (!activeTurn) {
            turn.status = "running";
            turn.completedAt = undefined;
            turn.activity.durationMs = undefined;
            if (turn.activity.summary === "작업 중단") turn.activity.summary = "작업 진행 중";
          }
          optimisticallyInterruptedTurn = undefined;
        }
        continue;
      }
      if (["failed", "interrupted", "completed"].includes(data.status)) {
        if (!data.optimistic) optimisticallyInterruptedTurn = undefined;
        const turn = findLatestActiveTurn(turns);
        if (!turn) continue;
        turn.status = data.status;
        turn.completedAt = eventTime;
        turn.activity.durationMs = durationBetween(turn.startedAt, turn.completedAt);
        turn.activity.summary = data.status === "failed"
          ? "작업 실패"
          : data.status === "interrupted"
            ? "작업 중단"
            : "작업 완료";
        if (data.status === "interrupted" && data.optimistic) optimisticallyInterruptedTurn = turn;
      }
      continue;
    }

    if (event.event === "error") {
      const turn = turns.at(-1);
      if (!turn) continue;
      turn.status = "failed";
      turn.completedAt = eventTime;
      turn.error = data.message ?? "Request failed";
      turn.activity.durationMs = durationBetween(turn.startedAt, turn.completedAt);
      turn.activity.summary = "작업 실패";
      continue;
    }

    if (event.event === "turn.started") {
      const turn = findTurn(turns, byTurnId, data.turnId, true, event.id, eventTime);
      bindTurnId(turn, data.turnId, byTurnId);
      setTurnPhase(turn, "running");
      turn.startedAt = turn.startedAt ?? data.startedAt ?? eventTime;
      continue;
    }

    if (event.event === "turn.completed") {
      const turn = findTurn(turns, byTurnId, data.turnId, false, event.id, eventTime);
      bindTurnId(turn, data.turnId, byTurnId);
      if (turn === optimisticallyInterruptedTurn) optimisticallyInterruptedTurn = undefined;
      turn.status = data.status ?? "completed";
      turn.startedAt = data.startedAt ?? turn.startedAt;
      turn.completedAt = data.completedAt ?? eventTime;
      turn.error = data.error;
      turn.activity.durationMs = data.durationMs
        ?? durationBetween(turn.startedAt, turn.completedAt);
      turn.activity.summary = turn.status === "failed"
        ? "작업 실패"
        : turn.status === "interrupted"
          ? "작업 중단"
          : "작업 완료";
      continue;
    }

    if (event.event === "session.output") {
      if (data.kind === "status") {
        const turn = turns.at(-1);
        if (!turn) continue;
        if (isUnscopedOptimisticTerminalStatus(data, turn)) continue;
        turn.status = /failed/i.test(data.text ?? "") ? "failed" : "completed";
        turn.completedAt = eventTime;
        turn.activity.durationMs = durationBetween(turn.startedAt, turn.completedAt);
        turn.activity.summary = turn.status === "failed" ? "작업 실패" : "작업 완료";
        continue;
      }
      if (!["assistant_delta", "assistant_snapshot"].includes(data.kind)) continue;
      const turn = findTurn(turns, byTurnId, data.turnId, false, event.id, eventTime);
      bindTurnId(turn, data.turnId, byTurnId);
      const messageItem = data.itemId ? turn.activity.items.get(data.itemId) : null;
      if (messageItem?.phase === "commentary") {
        messageItem.text = data.kind === "assistant_snapshot"
          ? data.text ?? ""
          : `${messageItem.text ?? ""}${data.text ?? ""}`;
        touchActivity(messageItem, eventTime);
        messageItem.summary = "진행 설명";
        turn.activity.summary = messageItem.text;
        continue;
      }
      turn.assistantText = data.kind === "assistant_snapshot"
        ? data.text ?? ""
        : `${turn.assistantText}${data.text ?? ""}`;
      continue;
    }

    if (event.event === "reasoning.delta") {
      const turn = findTurn(turns, byTurnId, data.turnId, false, event.id, eventTime);
      const item = upsertActivity(turn, data.itemId ?? "reasoning", {
        kind: "reasoning",
        status: "running",
        text: ""
      }, eventTime, eventTime);
      item.text += data.text ?? "";
      turn.activity.summary = item.text;
      continue;
    }

    if (event.event === "plan.updated") {
      const turn = findTurn(turns, byTurnId, data.turnId, false, event.id, eventTime);
      turn.activity.plan = data.plan ?? [];
      const active = turn.activity.plan.find((step) => step.status === "inProgress")
        ?? turn.activity.plan.find((step) => step.status === "pending");
      if (active?.step) turn.activity.summary = active.step;
      continue;
    }

    if (["activity.started", "activity.updated", "activity.completed"].includes(event.event)) {
      const turn = findTurn(turns, byTurnId, data.turnId, false, event.id, eventTime);
      const createdAt = Number.isFinite(data.startedAt) ? data.startedAt : eventTime;
      const updatedAt = event.event === "activity.completed" && Number.isFinite(data.completedAt)
        ? data.completedAt
        : eventTime;
      const item = upsertActivity(turn, data.itemId ?? event.id, {
        kind: data.kind ?? "activity",
        phase: data.phase,
        status: "running",
        text: ""
      }, createdAt, updatedAt);
      Object.assign(item, compact({
        kind: data.kind,
        phase: data.phase,
        status: data.status ?? (event.event === "activity.completed" ? "completed" : undefined),
        summary: data.summary,
        startedAt: data.startedAt,
        completedAt: data.completedAt,
        durationMs: data.durationMs,
        paths: data.paths,
        diffs: data.diffs,
        toolCall: data.toolCall,
        error: data.error,
        exitCode: data.exitCode,
        images: data.images
      }));
      if (data.text) item.text = event.event === "activity.completed" ? data.text : `${item.text ?? ""}${data.text}`;
      turn.activity.summary = data.summary ?? item.summary ?? turn.activity.summary;
      continue;
    }

    if (event.event === "files.changed") {
      const turn = turns.at(-1);
      if (turn) turn.activity.fileCount = data.files?.length ?? 0;
      continue;
    }

    if (["approval.requested", "approval.resolved"].includes(event.event)) {
      const turn = turns.at(-1);
      if (!turn) continue;
      const index = turn.approvals.findIndex((approval) => approval.id === data.id);
      if (index >= 0) turn.approvals[index] = data;
      else turn.approvals.push(data);
      if (turn.approvals.some((approval) => approval.status === "pending")) {
        turn.status = "waiting_approval";
        turn.activity.summary = "승인 대기 중";
      } else if (turn.status === "waiting_approval") {
        turn.status = "running";
      }
    }
  }

  for (const turn of turns) promoteCommentaryPrompt(turn);

  return turns.filter((turn) => turn.status !== "queued").map((turn) => ({
    id: turn.id,
    turnId: turn.turnId,
    queuedMessageId: turn.queuedMessageId,
    userText: turn.userText,
    userTexts: turn.userTexts,
    userMessages: turn.userMessages,
    userAttachments: turn.userAttachments,
    assistantText: visibleAssistantText(turn.assistantText),
    status: turn.status,
    startedAt: turn.startedAt,
    completedAt: turn.completedAt,
    error: turn.error,
    approvals: turn.approvals,
    activity: {
      summary: turn.activity.summary,
      items: [...turn.activity.items.values()].filter(isVisibleActivity),
      plan: turn.activity.plan,
      fileCount: turn.activity.fileCount,
      durationMs: turn.activity.durationMs
    }
  }));
}

function isUnscopedOptimisticTerminalStatus(data, turn) {
  return !data.turnId
    && turn.optimistic
    && ["sending", "thinking", "running"].includes(turn.status)
    && !turn.assistantText.trim()
    && !hasActivityLog(turn)
    && /completed/i.test(data.text ?? "");
}

function hasActivityLog(turn) {
  return turn.activity.plan.length > 0
    || [...turn.activity.items.values()].some(isVisibleActivity)
    || turn.approvals.some((approval) => approval.status !== "pending")
    || Boolean(turn.error);
}

function createTurn(id, userText, time, userAttachments = []) {
  return {
    id,
    userText,
    userTexts: userText ? [userText] : [],
    userMessages: userText ? [{ text: userText, createdAt: time, attachments: userAttachments }] : [],
    userAttachments,
    assistantText: "",
    status: "thinking",
    startedAt: time,
    approvals: [],
    activity: {
      summary: "작업 준비 중",
      items: new Map(),
      plan: [],
      fileCount: 0
    }
  };
}

function addUserMessage(turn, text, createdAt, attachments = [], { dedupeText = false } = {}) {
  if (!dedupeText || !turn.userTexts.includes(text)) turn.userTexts.push(text);
  if (!turn.userMessages.some((message) => message.text === text && message.createdAt === createdAt)) {
    turn.userMessages.push({ text, createdAt, attachments });
  }
}

function setTurnPhase(turn, status) {
  turn.status = status;
  if (status === "sending") {
    turn.activity.summary = "전송중";
    const latest = turn.userMessages.at(-1);
    if (latest) latest.status = "sending";
    return;
  }
  if (status === "thinking") {
    turn.activity.summary = "생각중";
  } else if (status === "running" && ["전송중", "생각중"].includes(turn.activity.summary)) {
    turn.activity.summary = "작업중";
  }
  for (const message of turn.userMessages) {
    if (message.status === "sending") delete message.status;
  }
}

function visibleUserText(text, attachmentCount = 0) {
  if (!isCodeRemoteManagerBootstrapPrompt(text)) return text || attachmentLabel(attachmentCount);
  const marker = "\nCurrent user request:\n";
  const index = text.indexOf(marker);
  if (index < 0) return null;
  const request = text.slice(index + marker.length).trim();
  return request || attachmentLabel(attachmentCount);
}

function visibleAssistantText(text) {
  return String(text ?? "").replace(/```[^\r\n`]*\r?\n\s*```/g, "").trim();
}

function attachmentLabel(count) {
  if (count === 1) return "Image attached";
  if (count > 1) return `${count} images attached`;
  return null;
}

function safeBrowserAttachments(attachments = [], sessionId = "") {
  return attachments
    .filter((item) => item?.previewUrl || item?.id)
    .map((item) => item.deleted
      ? { id: item.id, name: item.name, deleted: true }
      : {
          id: item.id,
          name: item.name,
          previewUrl: item.previewUrl ?? `${API_BASE}/sessions/${encodeURIComponent(sessionId)}/attachments/${encodeURIComponent(item.id)}`
        });
}

function isCodeRemoteManagerBootstrapPrompt(text) {
  return String(text).startsWith([
    "Continue this Code Remote Manager work in a new account-owned thread.",
    "Use the previous visible conversation as context."
  ].join("\n"));
}

function findTurn(turns, byTurnId, turnId, preferUnbound, eventId, eventTime) {
  if (turnId && byTurnId.has(turnId)) return byTurnId.get(turnId);
  const candidate = preferUnbound
    ? [...turns].reverse().find((turn) => !turn.turnId)
    : turns.at(-1);
  if (candidate) return candidate;
  const fallback = createTurn(eventId, "", eventTime);
  turns.push(fallback);
  return fallback;
}

function bindTurnId(turn, turnId, byTurnId) {
  if (!turnId) return;
  turn.turnId = turnId;
  byTurnId.set(turnId, turn);
}

function findLatestActiveTurn(turns) {
  return [...turns].reverse().find((turn) => ["sending", "thinking", "running", "waiting_approval", "waiting_response"].includes(turn.status));
}

function upsertActivity(turn, itemId, defaults, createdAt, updatedAt = createdAt) {
  if (!turn.activity.items.has(itemId)) {
    turn.activity.items.set(itemId, { id: itemId, createdAt, ...defaults });
  }
  const item = turn.activity.items.get(itemId);
  touchActivity(item, updatedAt);
  return item;
}

function touchActivity(item, updatedAt) {
  if (!Number.isFinite(updatedAt)) return;
  item.updatedAt = Math.max(item.updatedAt ?? item.createdAt ?? updatedAt, updatedAt);
}

function durationBetween(startedAt, completedAt) {
  if (!Number.isFinite(startedAt) || !Number.isFinite(completedAt)) return undefined;
  return Math.max(0, completedAt - startedAt);
}

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

function promoteCommentaryPrompt(turn) {
  if (turn.assistantText.trim() || turn.status !== "completed") return;
  const prompts = [...turn.activity.items.values()]
    .filter((item) => item.kind === "agentMessage" && item.phase === "commentary" && isResponsePrompt(item.text));
  const prompt = prompts.at(-1);
  if (!prompt) return;
  turn.assistantText = prompt.text.trim();
  turn.status = "waiting_response";
  turn.activity.summary = "응답 대기 중";
  turn.activity.items.delete(prompt.id);
}

function isResponsePrompt(text) {
  const value = String(text ?? "").trim();
  return /[?？]\s*$/.test(value)
    || /(원하|할까요|될까요|괜찮|확인|승인|선택|답(?:장|변)|respond|confirm|approve|choose|want to)/i.test(value);
}

function isVisibleActivity(item) {
  if (item.kind === "userMessage") return false;
  if (item.kind === "agentMessage") return item.phase === "commentary";
  return true;
}
