import { useCallback, useState } from "react";
import {
  ChevronDown,
  ChevronRight,
  Clock,
  ExternalLink,
  Loader2,
  MessageSquare,
  Trash2,
  User,
  Zap,
} from "lucide-react";
import type { JSX } from "react";
import { getSessionPath } from "../../lib/sessionUrl";
import { cn, formatTokens } from "../../lib/utils";
import type { CapturedLog } from "../../proxy/schemas";
import { Badge } from "../ui/badge";
import { ConfirmDialog } from "../ui/confirm-dialog";

const API_FORMAT_LABELS: Record<"anthropic" | "openai" | "unknown", string> = {
  anthropic: "Anthropic",
  openai: "OpenAI",
  unknown: "Unknown",
};

export type ViewMode = "flat" | "thread";

export type ConversationHeaderProps = {
  conversationId: string;
  startTime: string;
  endTime: string;
  totalCalls: number;
  totalInputTokens: number;
  totalOutputTokens: number;
  apiFormat: "anthropic" | "openai" | "unknown";
  expanded: boolean;
  onToggle: () => void;
  /** Hide the API format badge on the header (used when the group contains
   * mixed formats - the per-log badges are shown instead). */
  hideApiFormat?: boolean;
  /** When true and the group is collapsed, show a spinner instead of the
   * expand chevron to indicate an in-flight request inside the group. */
  isLoading?: boolean;
  /** User-Agent string from the first log in the group. */
  userAgent?: string | null;
  /** Clear all logs in this group. After confirmation the parent removes them
   *  from the in-memory store; this header is then unmounted. */
  onClear?: () => void;
};

function formatTimestamp(iso: string): string {
  const date = new Date(iso);
  return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
}

export function ConversationHeader({
  conversationId,
  startTime,
  endTime,
  totalCalls,
  totalInputTokens,
  totalOutputTokens,
  apiFormat,
  expanded,
  onToggle,
  hideApiFormat = false,
  isLoading = false,
  userAgent,
  onClear,
}: ConversationHeaderProps): JSX.Element {
  const [confirmOpen, setConfirmOpen] = useState(false);

  const handleClearClick = (e: React.MouseEvent | React.KeyboardEvent): void => {
    e.stopPropagation();
    if (onClear === undefined) return;
    setConfirmOpen(true);
  };

  const handleOpenInNewTab = useCallback(
    (e: React.MouseEvent | React.KeyboardEvent): void => {
      e.stopPropagation();
      window.open(getSessionPath(conversationId), "_blank", "noopener,noreferrer");
    },
    [conversationId],
  );

  return (
    <div
      role="button"
      tabIndex={0}
      data-nav-id={`conv-${conversationId}`}
      data-nav-action={expanded ? "collapse" : "expand"}
      className={cn(
        "flex items-center gap-3 px-3 py-2 cursor-pointer transition-colors",
        "hover:bg-muted/50",
        "select-none",
        "border border-border rounded-lg mb-2 bg-background sticky top-0 z-10",
        "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none",
      )}
      onClick={onToggle}
      onKeyDown={(e) => {
        if (e.target !== e.currentTarget) return;
        if (e.key === "Enter" || e.key === " ") {
          e.preventDefault();
          onToggle();
        }
      }}
    >
      {/* Expand chevron - shows spinner when collapsed and group has pending logs */}
      {expanded ? (
        <ChevronDown className="size-4 text-muted-foreground shrink-0" />
      ) : isLoading ? (
        <Loader2 className="size-4 animate-spin text-muted-foreground shrink-0" />
      ) : (
        <ChevronRight className="size-4 text-muted-foreground shrink-0" />
      )}

      {/* Conversation ID */}
      <span
        className="text-purple-400/90 font-mono text-xs font-semibold shrink-0"
        title={conversationId}
      >
        {conversationId.startsWith("PID:") || conversationId.includes("|")
          ? conversationId
          : conversationId.length > 24
            ? conversationId.slice(0, 12) + "..." + conversationId.slice(-12)
            : conversationId}
      </span>

      {/* User-Agent */}
      {userAgent !== null && userAgent !== undefined && userAgent !== "" && (
        <span
          className="flex items-center gap-1 text-muted-foreground text-xs shrink-0"
          title={userAgent}
        >
          <User className="size-3" />
          <span className="font-mono tabular-nums truncate max-w-[120px]">{userAgent}</span>
        </span>
      )}

      {/* API Format Badge */}
      {!hideApiFormat && (
        <Badge
          variant="outline"
          className={cn(
            "text-[10px] px-1.5 py-0 h-5 font-mono shrink-0",
            apiFormat === "openai" && "border-blue-500/40 text-blue-400",
            apiFormat === "anthropic" && "border-orange-500/40 text-orange-400",
            apiFormat === "unknown" && "border-muted text-muted-foreground",
          )}
        >
          {API_FORMAT_LABELS[apiFormat]}
        </Badge>
      )}

      {/* Time range */}
      <span className="flex items-center gap-1 text-muted-foreground text-xs shrink-0">
        <Clock className="size-3" />
        <span className="font-mono tabular-nums">
          {formatTimestamp(startTime)} - {formatTimestamp(endTime)}
        </span>
      </span>

      {/* Total calls */}
      <span className="flex items-center gap-1 text-muted-foreground text-xs shrink-0">
        <MessageSquare className="size-3" />
        <span className="font-mono tabular-nums">
          {totalCalls} call{totalCalls !== 1 ? "s" : ""}
        </span>
      </span>

      {/* Token counts */}
      <span className="flex items-center gap-1 text-xs shrink-0">
        <Zap className="size-3 text-muted-foreground" />
        <span className="font-mono tabular-nums">
          <span className="text-blue-400">{formatTokens(totalInputTokens)}</span>
          {" / "}
          <span className="text-amber-400">{formatTokens(totalOutputTokens)}</span>
        </span>
      </span>

      {/* Spacer */}
      <span className="flex-1 min-w-0" />

      {/* Open this session in a new tab - deep link to /session/$id */}
      <button
        type="button"
        onClick={handleOpenInNewTab}
        onKeyDown={(e) => {
          if (e.key === "Enter" || e.key === " ") {
            handleOpenInNewTab(e);
          }
        }}
        aria-label={`Open session ${conversationId} in a new tab`}
        title="Open this session in a new tab"
        className="text-muted-foreground hover:text-foreground transition-colors shrink-0 inline-flex items-center justify-center size-8 rounded hover:bg-muted cursor-pointer"
      >
        <ExternalLink className="size-3.5" />
      </button>

      {/* Per-group Clear - does not toggle the group's expand state */}
      {onClear !== undefined && (
        <button
          type="button"
          onClick={handleClearClick}
          aria-label={`Clear group (${totalCalls} request${totalCalls !== 1 ? "s" : ""})`}
          title="Clear this group"
          className="text-muted-foreground hover:text-foreground transition-colors shrink-0 inline-flex items-center justify-center size-8 rounded hover:bg-muted cursor-pointer"
        >
          <Trash2 className="size-3.5" />
        </button>
      )}

      <ConfirmDialog
        open={confirmOpen}
        onOpenChange={setConfirmOpen}
        title="Clear this group?"
        description={`This will remove ${totalCalls} request${totalCalls !== 1 ? "s" : ""} from this conversation. This action cannot be undone.`}
        confirmLabel="Clear"
        variant="destructive"
        onConfirm={() => {
          onClear?.();
        }}
      />
    </div>
  );
}

export type ConversationGroupData = {
  id: string;
  conversationId: string;
  logs: CapturedLog[];
};

export function getGroupApiFormat(logs: CapturedLog[]): "anthropic" | "openai" | "unknown" {
  const firstNonUnknown = logs.find((l) => l.apiFormat !== "unknown");
  if (firstNonUnknown !== undefined) {
    return firstNonUnknown.apiFormat;
  }
  return logs[0]?.apiFormat ?? "unknown";
}

/**
 * Returns true when a group contains logs of BOTH Anthropic and OpenAI
 * formats. Used to decide whether to render the per-row API format badge
 * inside a group (the badge is normally hidden because the group header
 * already shows the format).
 */
export function hasMixedApiFormat(logs: CapturedLog[]): boolean {
  let hasAnthropic = false;
  let hasOpenai = false;
  for (const log of logs) {
    if (log.apiFormat === "anthropic") hasAnthropic = true;
    else if (log.apiFormat === "openai") hasOpenai = true;
    if (hasAnthropic && hasOpenai) return true;
  }
  return false;
}

export function getConversationId(log: CapturedLog): string {
  if (log.isTest === true) return "provider-test";
  if (log.sessionId !== null && log.sessionId !== "" && log.sessionId !== undefined) {
    return log.sessionId;
  }
  const hasPid = log.clientPid !== null && log.clientPid !== undefined;
  const hasFolder = log.clientProjectFolder !== null && log.clientProjectFolder !== undefined;
  if (!hasPid && !hasFolder) return "default";
  const pid = hasPid ? `PID:${log.clientPid}` : "unknown";
  const folder = hasFolder ? log.clientProjectFolder : "no-folder";
  return `${pid}|${folder}`;
}

export function groupLogsByConversation(logs: CapturedLog[]): ConversationGroupData[] {
  const groups = new Map<string, CapturedLog[]>();

  for (const log of logs) {
    const convId = getConversationId(log);
    const existing = groups.get(convId);
    if (existing !== undefined) {
      existing.push(log);
    } else {
      groups.set(convId, [log]);
    }
  }

  const result: ConversationGroupData[] = [];

  for (const [conversationId, groupLogs] of groups) {
    const sorted = [...groupLogs].sort((a, b) => a.timestamp.localeCompare(b.timestamp));
    result.push({
      id: conversationId,
      conversationId,
      logs: sorted,
    });
  }

  result.sort((a, b) => (a.logs[0]?.timestamp ?? "").localeCompare(b.logs[0]?.timestamp ?? ""));

  return result;
}
