'use client';

import { type ReactNode, useEffect, useRef, useState } from 'react';
import { ChevronDown, ChevronRight, Loader2 } from 'lucide-react';

import { cn } from '@djangocfg/ui-core/lib';

import type { ChatToolCall } from '../types';

export type ToolPayloadKind = 'input' | 'output' | 'streaming';

export interface ToolCallsProps {
  calls: ChatToolCall[];
  /** Open every panel up-front. Default: false (panels are closed). */
  defaultExpanded?: boolean;
  /** Auto-open while a tool is running, then auto-close on completion.
   *  User toggles after that are remembered. Default: true. */
  expandWhileStreaming?: boolean;
  /** Override how the tool input payload is rendered. Receives the raw value. */
  renderInput?: (input: unknown, call: ChatToolCall) => ReactNode;
  /** Override how the tool output payload is rendered. */
  renderOutput?: (output: unknown, call: ChatToolCall) => ReactNode;
  /** Override how the live `streamingText` is rendered. */
  renderStreaming?: (text: string, call: ChatToolCall) => ReactNode;
  /** Single override for all three; specific renderers above take precedence. */
  renderPayload?: (value: unknown, kind: ToolPayloadKind, call: ChatToolCall) => ReactNode;
  className?: string;
}

export function ToolCalls({
  calls,
  defaultExpanded = false,
  expandWhileStreaming = true,
  renderInput,
  renderOutput,
  renderStreaming,
  renderPayload,
  className,
}: ToolCallsProps) {
  if (!calls?.length) return null;
  return (
    <div className={cn('mt-2 space-y-1.5', className)}>
      {calls.map((call) => (
        <ToolCallItem
          key={call.id}
          call={call}
          defaultExpanded={defaultExpanded}
          expandWhileStreaming={expandWhileStreaming}
          renderInput={renderInput}
          renderOutput={renderOutput}
          renderStreaming={renderStreaming}
          renderPayload={renderPayload}
        />
      ))}
    </div>
  );
}

interface ItemProps {
  call: ChatToolCall;
  defaultExpanded: boolean;
  expandWhileStreaming: boolean;
  renderInput?: ToolCallsProps['renderInput'];
  renderOutput?: ToolCallsProps['renderOutput'];
  renderStreaming?: ToolCallsProps['renderStreaming'];
  renderPayload?: ToolCallsProps['renderPayload'];
}

function ToolCallItem({
  call,
  defaultExpanded,
  expandWhileStreaming,
  renderInput,
  renderOutput,
  renderStreaming,
  renderPayload,
}: ItemProps) {
  const isRunning = call.status === 'running';
  const initialOpen = defaultExpanded || (expandWhileStreaming && isRunning);
  const [open, setOpen] = useState(initialOpen);
  // Remember manual interaction so completion doesn't override it.
  const userToggledRef = useRef(false);
  const wasRunningRef = useRef(isRunning);

  // Auto-collapse on running → completed transition, unless user has interacted.
  useEffect(() => {
    if (wasRunningRef.current && !isRunning) {
      if (!userToggledRef.current && !defaultExpanded) {
        setOpen(false);
      }
    }
    wasRunningRef.current = isRunning;
  }, [isRunning, defaultExpanded]);

  const handleToggle = () => {
    userToggledRef.current = true;
    setOpen((v) => !v);
  };

  const Icon = open ? ChevronDown : ChevronRight;
  const statusColor =
    call.status === 'success'
      ? 'text-emerald-500'
      : call.status === 'error'
        ? 'text-destructive'
        : call.status === 'cancelled'
          ? 'text-muted-foreground'
          : 'text-amber-500';

  const renderValue = (value: unknown, kind: ToolPayloadKind): ReactNode => {
    if (kind === 'input' && renderInput) return renderInput(value, call);
    if (kind === 'output' && renderOutput) return renderOutput(value, call);
    if (kind === 'streaming' && renderStreaming)
      return renderStreaming(typeof value === 'string' ? value : String(value), call);
    if (renderPayload) return renderPayload(value, kind, call);
    return <DefaultPayload value={value} kind={kind} />;
  };

  return (
    <div className="overflow-hidden rounded-md border border-border bg-muted/30">
      <button
        type="button"
        onClick={handleToggle}
        aria-expanded={open}
        className="flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs hover:bg-muted/60"
      >
        <Icon aria-hidden className="size-3 shrink-0 text-muted-foreground" />
        {isRunning ? (
          <Loader2 aria-hidden className="size-3 shrink-0 animate-spin text-amber-500" />
        ) : (
          <span className={cn('size-2 shrink-0 rounded-full', statusColor.replace('text-', 'bg-'))} />
        )}
        <span className="font-mono text-foreground">{call.name}</span>
        <span className={cn('ml-auto', statusColor)}>{call.status}</span>
      </button>
      {open ? (
        <div className="space-y-1 border-t border-border px-2 py-1.5 text-[11px]">
          {call.input != null ? renderValue(call.input, 'input') : null}
          {call.streamingText != null
            ? renderValue(call.streamingText, 'streaming')
            : call.output !== undefined
              ? renderValue(call.output, 'output')
              : null}
        </div>
      ) : null}
    </div>
  );
}

function DefaultPayload({ value, kind }: { value: unknown; kind: ToolPayloadKind }) {
  const isStreamingOrString = kind === 'streaming' || typeof value === 'string';
  const muted = kind === 'input';
  return (
    <pre
      className={cn(
        'overflow-auto rounded bg-background/60 p-1.5 font-mono',
        kind === 'input' ? 'max-h-32' : 'max-h-48',
        muted ? 'text-muted-foreground' : 'text-foreground/90',
      )}
    >
      {isStreamingOrString ? String(value) : safeStringify(value)}
    </pre>
  );
}

function safeStringify(value: unknown): string {
  try {
    return JSON.stringify(value, null, 2);
  } catch {
    return String(value);
  }
}
