// Tiny dispatcher for tool-call payload renderers.
//
// Hosts compose matchers (predicate + render) and a fallback. The result is a
// `renderPayload` function compatible with `<ToolCalls renderPayload>`.
//
// Pure — no React, no DOM. The matchers themselves return ReactNode.

import type { ReactNode } from 'react';

import type { ChatToolCall } from '../types';
import type { ToolPayloadKind } from '../messages/ToolCalls';

export interface ToolPayloadMatcher {
  /** Cheap predicate. First match wins. */
  match: (value: unknown, kind: ToolPayloadKind, call: ChatToolCall) => boolean;
  render: (value: unknown, kind: ToolPayloadKind, call: ChatToolCall) => ReactNode;
}

export type ToolPayloadFallback = (
  value: unknown,
  kind: ToolPayloadKind,
  call: ChatToolCall,
) => ReactNode;

export function dispatchToolPayload(
  matchers: ToolPayloadMatcher[],
  fallback: ToolPayloadFallback,
): ToolPayloadFallback {
  return (value, kind, call) => {
    for (const m of matchers) {
      if (m.match(value, kind, call)) return m.render(value, kind, call);
    }
    return fallback(value, kind, call);
  };
}

// ---- Common matcher predicates -------------------------------------------
// Re-export as building blocks. Hosts can use directly or compose.

export function isPlainObject(v: unknown): v is Record<string, unknown> {
  return v !== null && typeof v === 'object' && !Array.isArray(v);
}

export function isLatLng(v: unknown): v is { lat: number; lng: number } {
  return isPlainObject(v) && typeof v.lat === 'number' && typeof v.lng === 'number';
}

export function isGeoJSONFeatureCollection(v: unknown): v is { type: 'FeatureCollection'; features: unknown[] } {
  return isPlainObject(v) && v.type === 'FeatureCollection' && Array.isArray(v.features);
}

export function isStringValue(v: unknown): v is string {
  return typeof v === 'string';
}
