'use client';

/**
 * ToolPayloadValue — the single, shared renderer for a tool call's
 * `input` / `output` / live `streaming` payload.
 *
 * Routing (one implementation, reused everywhere):
 *   - object / array (and NOT streaming) → interactive collapsible
 *     `LazyJsonTree` (the same viewer the chat `json` block + document
 *     inspector use). It renders values as React children — never
 *     `innerHTML` — so a tool result carrying HTML/markdown (e.g. a fetched
 *     web page) is shown as text, not executed: XSS-safe.
 *   - string / scalar / streaming → plain `<pre>` text. A streaming payload
 *     arrives as a partial string and must NOT be parsed mid-flight; prose /
 *     scalars have no tree to show.
 *
 * Both the default `DefaultPayload` (inside `ToolCalls`) AND a host that owns
 * its own tool-call card (e.g. cmdop's `<ToolResult>` action card, reached via
 * the `renderToolCall` override) render THROUGH this — so the object→tree
 * behaviour can't drift between the two. `surfaceClassName` lets a host match
 * its own card's surface token (the only thing that differed when the two
 * implementations were separate).
 */

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

import { LazyJsonTree } from '../../data/JsonTree/lazy';

// Defined locally (NOT imported from ./ToolCalls) on purpose: ToolCalls
// imports ToolPayloadValue, so importing the type back would form an import
// cycle. Under the `'use client'` + subpath-barrel (`./chat` → lazy.tsx) setup
// that cycle makes Vite drop this module's symbol from the re-export graph at
// runtime (the export typechecks via index.ts but fails to bundle — the exact
// `MarkdownMessage` gotcha documented in lazy.tsx). The union is the SAME shape
// ToolCalls re-exports as `ToolPayloadKind`; keep them in sync (trivial).
export type ToolPayloadKind = 'input' | 'output' | 'streaming';

/** A structured value worth showing in the JSON tree (object or array). */
function isStructured(value: unknown): value is object {
  return value !== null && typeof value === 'object';
}

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

export interface ToolPayloadValueProps {
  value: unknown;
  /** Drives expand depth (input collapsed, output 1 level), muted text, and
   *  whether a streaming partial is parsed (it never is). */
  kind: ToolPayloadKind;
  /** Surface token for the tree wrapper + `<pre>` background. Defaults to the
   *  chat panel's `bg-background/60`; a host card can pass e.g. `bg-muted/40`. */
  surfaceClassName?: string;
  /**
   * Show the JsonTree toolbar (search / expand / copy). Default **false**:
   * the tool-call panel already has its own header (tool name + status), so a
   * second toolbar is visual noise — and in `'auto'` mode JsonTree RESERVES the
   * toolbar's height even while hidden, leaving an empty gap above the tree.
   * Opt in (`toolbar`) only when copy/search on the raw payload is worth the
   * extra chrome. When true the toolbar is hover-revealed (`'auto'`).
   */
  toolbar?: boolean;
}

export function ToolPayloadValue({
  value,
  kind,
  surfaceClassName = 'bg-background/60',
  toolbar = false,
}: ToolPayloadValueProps) {
  if (kind !== 'streaming' && isStructured(value)) {
    return (
      <div className={cn('overflow-auto rounded', surfaceClassName)}>
        <LazyJsonTree
          data={value}
          bordered={false}
          size="sm"
          // Input collapsed, output 1 level. Toolbar OFF by default — the
          // tool-call panel owns the header; `auto` would reserve height and
          // leave an empty gap above the tree (see the `toolbar` prop note).
          defaultExpandedDepth={kind === 'input' ? 0 : 1}
          actions={['search', 'expand', 'copy']}
          toolbar={toolbar ? 'auto' : 'never'}
        />
      </div>
    );
  }

  return (
    <pre
      className={cn(
        'overflow-auto rounded p-1.5 font-mono',
        surfaceClassName,
        kind === 'input' ? 'max-h-32' : 'max-h-48',
        kind === 'input' ? 'text-muted-foreground' : 'text-foreground/90',
      )}
    >
      {typeof value === 'string' ? value : safeStringify(value)}
    </pre>
  );
}
