/**
 * `ConversationViewer` — human, read-only transcript of one conversation pair
 * (LS7). Sober by directive: one bubble per turn, full text, oldest→newest, with
 * a discreet User↔Agent / Agent↔Agent distinction (two neutral tints, no
 * avatars). Tool activity is dimmed and collapsed by default.
 *
 * Cold human surface: it lives behind a click in the Work panel and swaps the
 * terminal grid in local state — zero impact on the agent hot path.
 */

import { useMemo, useState } from "react";

import type { TurnSource, TurnView } from "@/domain";
import { Button, Spinner, cn } from "@/shared";
import { useProjectWorkState } from "@/features/workstate";
import { useConversationThread } from "./useConversationThread";

export interface ConversationViewerProps {
  projectId: string;
  conversationId: string;
  onClose: () => void;
}

function shortId(id: string): string {
  return id.length <= 8 ? id : id.slice(0, 8);
}

/** Stable key for a turn's party (the conversation has at most two). */
function partyKey(source: TurnSource): string {
  return source.kind === "human" ? "human" : `agent:${source.agentId}`;
}

function formatTime(atMs: number): string {
  if (!Number.isFinite(atMs) || atMs <= 0) return "";
  try {
    return new Date(atMs).toLocaleString();
  } catch {
    return "";
  }
}

function roleLabel(role: TurnView["role"]): string {
  switch (role) {
    case "prompt":
      return "Prompt";
    case "response":
      return "Response";
    case "toolActivity":
      return "Tool";
  }
}

/** One collapsible tool-activity turn (dimmed, folded by default). */
function ToolTurn({ turn, sideClass }: { turn: TurnView; sideClass: string }) {
  const [open, setOpen] = useState(false);
  return (
    <div className={cn("flex flex-col", sideClass)}>
      <div className="max-w-[80%] rounded-md border border-border bg-surface/60 px-3 py-1.5 text-xs text-muted">
        <button
          type="button"
          className="flex items-center gap-1.5 font-medium hover:text-content"
          onClick={() => setOpen((v) => !v)}
          aria-expanded={open}
        >
          <span aria-hidden>{open ? "▾" : "▸"}</span>
          Tool activity
          <span className="text-faint">· {turn.textLen} chars</span>
        </button>
        {open && (
          <pre className="mt-1.5 whitespace-pre-wrap break-words font-mono text-[11px] text-muted">
            {turn.text}
          </pre>
        )}
      </div>
    </div>
  );
}

interface PartyMeta {
  /** "User" or the resolved agent name / short id. */
  label: string;
  /** Visual side: first party seen is left, second is right. */
  side: "left" | "right";
}

export function ConversationViewer({
  projectId,
  conversationId,
  onClose,
}: ConversationViewerProps) {
  const vm = useConversationThread(projectId, conversationId);
  // Reuse the already-loaded work-state inventory to resolve agent names.
  const work = useProjectWorkState(projectId);

  const agentNames = useMemo(() => {
    const map = new Map<string, string>();
    for (const agent of work.state?.agents ?? []) {
      if (agent.name.trim()) map.set(agent.agentId, agent.name.trim());
    }
    return map;
  }, [work.state]);

  // Derive the (at most two) parties from the turns, in first-appearance order.
  const parties = useMemo(() => {
    const order: string[] = [];
    const meta = new Map<string, PartyMeta>();
    for (const turn of vm.turns) {
      const key = partyKey(turn.source);
      if (meta.has(key)) continue;
      const side: PartyMeta["side"] = order.length === 0 ? "left" : "right";
      const label =
        turn.source.kind === "human"
          ? "User"
          : agentNames.get(turn.source.agentId) ??
            `Agent ${shortId(turn.source.agentId)}`;
      meta.set(key, { label, side });
      order.push(key);
    }
    return { order, meta };
  }, [vm.turns, agentNames]);

  const title = useMemo(() => {
    const labels = parties.order.map((k) => parties.meta.get(k)?.label ?? "?");
    if (labels.length >= 2) return `${labels[0]} ↔ ${labels[1]}`;
    if (labels.length === 1) return labels[0];
    return "Conversation";
  }, [parties]);

  function sideOf(source: TurnSource): "left" | "right" {
    return parties.meta.get(partyKey(source))?.side ?? "left";
  }

  return (
    <div className="flex flex-1 flex-col overflow-hidden">
      {/* ── Header ── */}
      <div className="flex shrink-0 items-center justify-between gap-3 border-b border-border bg-surface px-4 py-2">
        <div className="flex min-w-0 flex-col">
          <span className="truncate text-sm font-medium text-content">
            {title}
          </span>
          <code
            className="truncate text-xs text-muted"
            title={conversationId}
          >
            {shortId(conversationId)}
          </code>
        </div>
        <div className="flex shrink-0 items-center gap-2">
          <Button
            size="sm"
            variant="ghost"
            onClick={() => void vm.refreshTail()}
            loading={vm.busy}
          >
            Refresh
          </Button>
          <Button size="sm" variant="ghost" onClick={onClose}>
            ← Retour aux terminaux
          </Button>
        </div>
      </div>

      {/* ── Error ── */}
      {vm.error && (
        <p
          role="alert"
          className="mx-4 mt-2 shrink-0 rounded-md border border-danger/40 bg-danger/10 px-3 py-2 text-sm text-danger"
        >
          {vm.error}
        </p>
      )}

      {/* ── Thread ── */}
      <div className="flex flex-1 flex-col gap-2 overflow-auto px-4 py-3">
        {vm.hasMore && (
          <div className="flex justify-center">
            <Button
              size="sm"
              variant="ghost"
              onClick={() => void vm.loadOlder()}
              loading={vm.busy}
            >
              Charger les tours précédents
            </Button>
          </div>
        )}

        {vm.busy && vm.turns.length === 0 ? (
          <div className="flex items-center gap-2 text-sm text-muted">
            <Spinner size={14} />
            <span>Chargement du fil…</span>
          </div>
        ) : vm.turns.length === 0 ? (
          <p className="text-sm text-muted">Aucun tour pour le moment.</p>
        ) : (
          vm.turns.map((turn) => {
            const side = sideOf(turn.source);
            const sideClass = side === "right" ? "items-end" : "items-start";
            if (turn.role === "toolActivity") {
              return (
                <ToolTurn key={turn.id} turn={turn} sideClass={sideClass} />
              );
            }
            const party = parties.meta.get(partyKey(turn.source));
            const time = formatTime(turn.atMs);
            return (
              <div key={turn.id} className={cn("flex flex-col", sideClass)}>
                <div
                  className={cn(
                    "max-w-[80%] rounded-lg border px-3 py-2 text-sm",
                    side === "right"
                      ? "border-primary/20 bg-primary/10 text-content"
                      : "border-border bg-raised text-content",
                  )}
                >
                  <div className="mb-1 flex items-baseline gap-2 text-xs text-muted">
                    <span className="font-medium text-content">
                      {party?.label ?? "?"}
                    </span>
                    <span>· {roleLabel(turn.role)}</span>
                    {time && <span className="text-faint">· {time}</span>}
                  </div>
                  <p className="whitespace-pre-wrap break-words">{turn.text}</p>
                </div>
              </div>
            );
          })
        )}
      </div>
    </div>
  );
}
