import type { ReactElement } from "react";
import { useEffect, useMemo, useRef } from "react";

import { useScrollFollow } from "../../hooks/useScrollFollow";
import type { ChatMessage, CodeAgentAvailability, LiveSurface } from "../../types/domain";
import { formatBeijingTime, jsonPreview, shortToken, toneClass } from "../../utils";
import { codeAgentSummaryRows, classifyCodeAgentStatusSummary, codeAgentRuntimePathFromMessage } from "../../state/code-agent-status";
import { agentStatusLabel } from "../../state/agent-status-labels";
import { StateTag } from "../shared/StateTag";
import { DebugDialog, type DebugRow, type DebugSection } from "../shared/DebugDialog";
import { MessageMarkdown } from "../shared/MessageMarkdown";
import { MessageTracePanel } from "./MessageTracePanel";
import { MessageActions } from "./MessageActions";

interface ConversationPanelProps {
  messages: ChatMessage[];
  loading: boolean;
  availability: CodeAgentAvailability | null;
  live: LiveSurface | null;
  chatPending: boolean;
  onCopySession(): void;
  onCancelMessage(messageId: string): void;
  onRetryMessage(messageId: string): void;
  onReplayTrace(messageId: string): void;
}

const EMPTY_MESSAGE_ID_SET = new Set<string>();

export function ConversationPanel({ messages, loading, availability, live, chatPending, codeAgentTimeoutMs, onCopySession, onCancelMessage, onRetryMessage, onReplayTrace }: ConversationPanelProps & { codeAgentTimeoutMs: number }): ReactElement {
  const scrollFollow = useScrollFollow<HTMLElement>([messages]);
  const firstScreenMessageIds = useFirstScreenMessageIds(messages, loading);
  return (
    <section className="view workbench-view" id="workspace" data-view="workspace" data-scroll-follow={scrollFollow.following ? "bottom" : "detached"} aria-labelledby="workspace-title" ref={scrollFollow.ref} onScroll={scrollFollow.onScroll}>
      <div className="conversation-column">
        <div className="workspace-panel conversation-panel">
          <div className="panel-title-row">
            <div>
              <p className="eyebrow">Agent 工作区</p>
              <h2 id="workspace-title">Agent 对话</h2>
            </div>
            <div className="panel-title-actions">
              <button className="icon-button" id="copy-session-id" type="button" aria-label="复制 session id" title="复制当前 Code Agent session id" onClick={onCopySession}>复制 Session</button>
              <StateTag id="agent-chat-status" tone={chatPending ? "pending" : "source"}>{chatPending ? "处理中" : "等待输入"}</StateTag>
              <CodeAgentSummary availability={availability} live={live} latestMessage={latestAgentMessage(messages)} />
            </div>
          </div>
          <div className="conversation-list" id="conversation-list" aria-live="polite">
            {messages.length === 0 ? (
              <p className="conversation-empty" id="conversation-empty" data-conversation-empty>暂无对话。在下方 Command Bar 输入任务，Agent 回复会出现在这里。</p>
            ) : null}
            {messages.map((message) => (
              <MessageCard
                key={message.id}
                message={message}
                firstScreenHistory={firstScreenMessageIds.has(message.id)}
                codeAgentTimeoutMs={codeAgentTimeoutMs}
                onCancel={() => onCancelMessage(message.id)}
                onRetry={() => onRetryMessage(message.id)}
                onReplayTrace={() => onReplayTrace(message.id)}
              />
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}

function useFirstScreenMessageIds(messages: ChatMessage[], loading: boolean): Set<string> {
  const capturedRef = useRef<Set<string> | null>(null);
  if (!loading && capturedRef.current === null) {
    capturedRef.current = new Set(messages.map((message) => message.id));
  }
  return capturedRef.current ?? EMPTY_MESSAGE_ID_SET;
}

function latestAgentMessage(messages: ChatMessage[]): Record<string, unknown> | null {
  for (let index = messages.length - 1; index >= 0; index -= 1) {
    const message = messages[index];
    if (message?.role === "agent") return { ...message };
  }
  return null;
}

function CodeAgentSummary({ availability, live, latestMessage }: { availability: CodeAgentAvailability | null; live: LiveSurface | null; latestMessage: Record<string, unknown> | null }): ReactElement {
  const summary = useMemo(() => classifyCodeAgentStatusSummary({ availability, live, latestMessage }), [availability, live, latestMessage]);
  const rows = useMemo<DebugRow[]>(() => codeAgentSummaryRows(summary).map((row) => ({ label: row.key, value: row.value, tone: row.tone as DebugRow["tone"] })), [summary]);
  const sections: DebugSection[] = [
    { title: "工作区", rows: [
      { label: "webEntry", value: "http://74.48.78.17:19666/" },
      { label: "traceRenderer", value: "hwlab-cli client agent trace --render web" }
    ] },
    { title: "Code Agent 详情", rows },
    { title: "readiness blockers", rows: summary.readinessBlockers.length > 0
        ? summary.readinessBlockers.map((blocker) => ({ label: blocker, value: blocker, tone: "blocked" as const }))
        : [] , empty: summary.readinessBlockers.length === 0 ? "无" : undefined }
  ];
  return (
    <div className="code-agent-debug-summary" data-code-agent-status-kind={summary.kind}>
      <span className={`code-agent-summary-icon tone-${toneClass(summary.tone)}`} id="code-agent-summary-icon" aria-hidden="true">{summary.icon}</span>
      <span className={`code-agent-summary-label tone-${toneClass(summary.tone)}`} id="code-agent-summary-label">{summary.label}</span>
      <span className="code-agent-summary-capability" id="code-agent-summary-capability">{summary.capabilityLevel}</span>
      <DebugDialog
        dialogId="code-agent-debug"
        title="Code Agent 调试信息"
        sections={sections}
        rawJson={availability ?? summary.fields}
        triggerLabel="调试信息"
        triggerTitle="查看 Code Agent 状态、readiness blockers 和原始 JSON"
        className="code-agent-summary-debug"
      />
    </div>
  );
}

export function shouldAutoReplayTrace(message: ChatMessage): boolean {
  if (message.role !== "agent") return false;
  const trace = message.runnerTrace;
  const traceId = message.traceId ?? trace?.traceId;
  if (!traceId) return false;
  if (message.status === "running" || trace?.status === "running") return false;
  if (message.traceReplayStatus) return false;
  const events = Array.isArray(trace?.events) ? trace.events : [];
  if (trace?.fullTraceLoaded === true && events.length > 0) return false;
  const declaredCount = Number(trace?.eventCount ?? 0);
  return events.length === 0 && declaredCount > 0;
}

const HISTORICAL_TRACE_REPLAY_DELAY_MS = 4500;

function MessageCard({ message, firstScreenHistory, codeAgentTimeoutMs, onCancel, onRetry, onReplayTrace }: { message: ChatMessage; firstScreenHistory: boolean; codeAgentTimeoutMs: number; onCancel(): void; onRetry(): void; onReplayTrace(): void }): ReactElement {
  const traceKey = `hwlab.workbench.trace-open.${message.traceId ?? message.id}`;
  const replayRequestedTraceRef = useRef<string | null>(null);
  const replayHandlerRef = useRef(onReplayTrace);
  useEffect(() => {
    replayHandlerRef.current = onReplayTrace;
  }, [onReplayTrace]);
  const traceId = message.traceId ?? message.runnerTrace?.traceId ?? null;
  const shouldReplayTrace = shouldAutoReplayTrace(message);
  useEffect(() => {
    if (traceId && replayRequestedTraceRef.current === traceId) return;
    if (!shouldReplayTrace) return;
    const timer = window.setTimeout(() => {
      replayRequestedTraceRef.current = traceId;
      replayHandlerRef.current();
    }, HISTORICAL_TRACE_REPLAY_DELAY_MS);
    return () => window.clearTimeout(timer);
  }, [shouldReplayTrace, traceId]);
  if (message.role === "agent") {
    const hasFinalResponse = message.status !== "running" && message.text.trim().length > 0;
    return (
      <article className={`message-card message-${message.role} tone-border-${toneClass(message.status)}`} data-message-status={message.status}>
        {message.runnerTrace ? (
          <MessageTracePanel
            trace={message.runnerTrace}
            defaultOpen={message.status === "running"}
            storageKey={traceKey}
            collapseWhenFinalResponse={hasFinalResponse}
          />
        ) : null}
        <section className="message-final-response" aria-label="Final Response" data-first-screen-history={firstScreenHistory ? "true" : undefined}>
          {hasFinalResponse ? <MessageMarkdown>{message.text}</MessageMarkdown> : null}
        </section>
        <MessageDebug
          message={message}
          codeAgentTimeoutMs={codeAgentTimeoutMs}
          onCancel={onCancel}
          onRetry={onRetry}
        />
      </article>
    );
  }
  return (
    <article className={`message-card message-${message.role} tone-border-${toneClass(message.status)}`} data-message-status={message.status}>
      <MessageMarkdown>{message.text}</MessageMarkdown>
      <MessageDebug
        message={message}
        codeAgentTimeoutMs={codeAgentTimeoutMs}
        onCancel={onCancel}
        onRetry={onRetry}
      />
    </article>
  );
}

function MessageDebug({ message, codeAgentTimeoutMs, onCancel, onRetry }: { message: ChatMessage; codeAgentTimeoutMs: number; onCancel(): void; onRetry(): void }): ReactElement | null {
  if (message.role === "user" || message.role === "system") {
    if (!message.traceId) return null;
    return (
      <footer className="message-meta message-meta-compact">
        <span>{formatBeijingTime(message.updatedAt ?? message.createdAt, { withSeconds: true })}</span>
        <DebugDialog
          dialogId={`message-debug-${message.id}`}
          title="会话调试信息"
          triggerLabel="调试信息"
          triggerTitle="查看会话 traceId、threadId 等调试信息"
          sections={systemDebugSections(message)}
        />
      </footer>
    );
  }
  return (
    <>
      <footer className="message-meta message-meta-compact">
        <StateTag tone={statusLabel(message).tone}>{statusLabel(message).label}</StateTag>
        <span>{formatBeijingTime(message.updatedAt ?? message.createdAt, { withSeconds: true })}</span>
        <MessageActions message={message} onCancel={onCancel} onRetry={onRetry} />
        <DebugDialog
          dialogId={`message-debug-${message.id}`}
          title="Agent 消息调试信息"
          triggerLabel="调试信息"
          triggerTitle="查看会话 traceId、sessionId、threadId、runtime path 和 readiness 详情"
          sections={agentDebugSections(message, codeAgentTimeoutMs)}
        />
      </footer>
      {message.status === "running" ? (
        <p className="message-pending-inline" data-message-pending-inline>
          <span className={`state-tag tone-pending`}>处理中</span>
          <span className="message-pending-inline-text">{message.runnerTrace?.waitingFor ? `等待 ${message.runnerTrace.waitingFor}` : "等待后端事件"}</span>
        </p>
      ) : null}
    </>
  );
}

function systemDebugSections(message: ChatMessage): DebugSection[] {
  const rows: DebugRow[] = [];
  if (message.traceId) rows.push({ label: "traceId", value: message.traceId, copyable: true });
  return [{ title: "会话标识", rows }];
}

function agentDebugSections(message: ChatMessage, codeAgentTimeoutMs: number): DebugSection[] {
  const sessionRows: DebugRow[] = [];
  if (message.traceId) sessionRows.push({ label: "traceId", value: message.traceId, copyable: true });
  if (message.conversationId) sessionRows.push({ label: "conversation", value: message.conversationId, copyable: true });
  if (message.sessionId) sessionRows.push({ label: "session", value: message.sessionId, copyable: true });
  else if (message.runnerTrace?.sessionId) sessionRows.push({ label: "session", value: String(message.runnerTrace.sessionId), copyable: true });
  if (message.threadId) sessionRows.push({ label: "thread", value: message.threadId, copyable: true });
  else if (message.runnerTrace?.threadId) sessionRows.push({ label: "thread", value: String(message.runnerTrace.threadId), copyable: true });
  const sessionStatus = typeof message.runnerTrace?.sessionStatus === "string" ? message.runnerTrace.sessionStatus : message.status;
  if (sessionStatus) sessionRows.push({ label: "sessionStatus", value: String(sessionStatus) });
  const lifecycle = typeof message.runnerTrace?.sessionLifecycleStatus === "string" ? message.runnerTrace.sessionLifecycleStatus : null;
  if (lifecycle) sessionRows.push({ label: "lifecycle", value: lifecycle });
  if (message.runnerTrace?.lastEventLabel) sessionRows.push({ label: "lastEvent", value: String(message.runnerTrace.lastEventLabel) });
  sessionRows.push({ label: "activityTimeout", value: `${Math.round(codeAgentTimeoutMs / 60000)}m 无新事件` });

  const runtime = codeAgentRuntimePathFromMessage({ role: "agent", ...(message as unknown as Record<string, unknown>) });
  const runtimeRows: DebugRow[] = [
    { label: "provider", value: runtime.rows[0]?.value ?? "未观测" },
    { label: "runnerKind", value: runtime.rows[1]?.value ?? "未观测" },
    { label: "protocol", value: runtime.rows[2]?.value ?? "未观测" },
    { label: "implementationType", value: runtime.rows[3]?.value ?? "未观测" },
    { label: "providerTrace.command", value: runtime.rows[4]?.value ?? "未观测" },
    { label: "providerTrace.terminalStatus", value: runtime.rows[5]?.value ?? "未观测" },
    { label: "providerTrace.failureKind", value: runtime.rows[6]?.value ?? "未观测" }
  ].filter((row) => row.value !== "未观测" && row.value !== "字段缺失：证据不足");
  const runtimeSection: DebugSection = { title: "Runtime Path", rows: runtimeRows, empty: runtime.missingFields.length > 0 ? "未观测到 providerTrace 字段，跳过该 section。" : "运行路径不可用。" };

  return [
    { title: "会话标识", rows: sessionRows },
    runtimeSection
  ];
}

function statusLabel(message: ChatMessage): { label: string; tone: ReturnType<typeof toneClass> } {
  const label = agentStatusLabel(message);
  return { label: label.label, tone: label.tone };
}
