const ACTIVE_STATUSES = Object.freeze(["running", "pending", "accepted", "processing"]);
const COMPLETED_STATUSES = Object.freeze(["completed", "source", "idle", "ready", "active"]);
const UNUSABLE_SESSION_STATUSES = Object.freeze(["blocked", "expired", "stale", "thread-resume-failed"]);
const TERMINAL_STATUSES = Object.freeze([...COMPLETED_STATUSES, ...UNUSABLE_SESSION_STATUSES]);

export function computeCodeAgentComposerState(input = {}) {
  const latest = latestAgentMessage(input.messages ?? input.chatMessages);
  const current = input.currentRequest && typeof input.currentRequest === "object" ? input.currentRequest : null;
  const workspace = input.workspace && typeof input.workspace === "object" ? input.workspace : null;
  const explicitStatus = normalizedStatus(input.status ?? input.sessionStatus);
  const latestStatus = normalizedStatus(latest?.status);
  const effectiveStatus = latestStatus || explicitStatus;
  const traceId = firstNonEmptyString(
    input.targetTraceId,
    current?.traceId,
    runningTraceId(input.messages ?? input.chatMessages),
    workspace?.activeTraceId,
    latest?.status === "running" ? latest?.traceId : null
  );
  const active = Boolean(traceId) && !terminalStatus(effectiveStatus) && (
    activeStatus(current?.status) ||
    activeStatus(latest?.status) ||
    activeStatus(explicitStatus) ||
    (firstNonEmptyString(input.activeTraceId, workspace?.activeTraceId) === traceId && !terminalStatus(explicitStatus)) ||
    input.chatPending === true
  );
  const mode = active ? "steer" : "turn";
  const sessionId = firstNonEmptyString(input.sessionId, current?.sessionId, latest?.sessionId);
  const sessionUsable = Boolean(sessionId) && !unusableSessionStatus(explicitStatus);
  const disabledReason = active
    ? null
    : !sessionId
      ? "session_required"
      : unusableSessionStatus(explicitStatus)
        ? "session_not_usable"
        : null;
  return pruneUndefined({
    contract: "hwlab-code-agent-composer-policy-v1",
    webEquivalent: true,
    locked: false,
    disabled: Boolean(disabledReason),
    disabledReason,
    sessionRequired: !active && !sessionId,
    sessionUsable,
    submitMode: mode,
    mode,
    route: mode === "steer" ? "/v1/agent/chat/steer" : "/v1/agent/chat",
    method: "POST",
    targetTraceId: active ? traceId : null,
    sessionId,
    threadId: firstNonEmptyString(input.threadId, current?.threadId, latest?.threadId),
    reason: active ? "active-turn-steer" : disabledReason ?? (terminalStatus(effectiveStatus) ? "terminal-turn" : "idle-turn")
  });
}

export function isCodeAgentSessionUsableStatus(value) {
  const status = normalizedStatus(value);
  return !status || COMPLETED_STATUSES.includes(status);
}

export function isCodeAgentSessionUnusableStatus(value) {
  return unusableSessionStatus(value);
}

export function latestAgentMessage(messages = []) {
  if (!Array.isArray(messages)) return null;
  for (const message of [...messages].reverse()) {
    if (message?.role === "agent") return message;
  }
  return null;
}

function runningTraceId(messages = []) {
  if (!Array.isArray(messages)) return null;
  for (const message of [...messages].reverse()) {
    if (message?.status !== "running") continue;
    const traceId = firstNonEmptyString(message?.traceId, message?.runnerTrace?.traceId);
    if (traceId) return traceId;
  }
  return null;
}

function activeStatus(value) {
  return ACTIVE_STATUSES.includes(normalizedStatus(value));
}

function terminalStatus(value) {
  return TERMINAL_STATUSES.includes(normalizedStatus(value));
}

function unusableSessionStatus(value) {
  return UNUSABLE_SESSION_STATUSES.includes(normalizedStatus(value));
}

function normalizedStatus(value) {
  return String(value ?? "").trim().toLowerCase();
}

function firstNonEmptyString(...values) {
  for (const value of values) {
    const text = String(value ?? "").trim();
    if (text) return text;
  }
  return null;
}

function pruneUndefined(value) {
  const out = {};
  for (const [key, item] of Object.entries(value)) {
    if (item !== undefined) out[key] = item;
  }
  return out;
}
