'use client';

import React, { memo } from 'react';
import { CommonExternalProps, JSONTree } from 'react-json-tree';
import { useResolvedTheme } from '@djangocfg/ui-core/hooks';

// JSON inspector keeps its own syntax-highlight contrast model rather
// than reusing semantic UI tokens — same convention as Chrome DevTools,
// Insomnia and Bruno. It still follows the host light/dark theme, but
// each mode gets a hand-tuned base16 palette so keys/values stay
// high-contrast instead of flattening into pastels.
interface JsonTreePalette {
  surface: string;
  fg: string; // base05 — default text
  title: string; // base06 — title
  muted: string; // base04 — collection counts, meta
  string: string; // base0A — strings + link color
  theme: Record<string, string>;
}

const DARK_PALETTE: JsonTreePalette = {
  surface: '#0d1117',
  fg: '#e5e7eb',
  title: '#f3f4f6',
  muted: '#9ca3af',
  string: '#eab308',
  theme: {
    scheme: 'djangocfg-dark',
    base00: 'transparent',
    base01: '#1a1a1a',
    base02: '#2a2a2a',
    base03: '#6b7280',
    base04: '#9ca3af',
    base05: '#e5e7eb',
    base06: '#f3f4f6',
    base07: '#ffffff',
    base08: '#ef4444', // null, undefined
    base09: '#f97316', // numbers
    base0A: '#eab308', // strings
    base0B: '#22c55e', // booleans
    base0C: '#06b6d4', // dates, regex
    base0D: '#3b82f6', // keys
    base0E: '#a855f7', // functions
    base0F: '#f43f5e', // deprecations
  },
};

const LIGHT_PALETTE: JsonTreePalette = {
  surface: '#ffffff',
  fg: '#1f2937',
  title: '#111827',
  muted: '#6b7280',
  string: '#b45309',
  theme: {
    scheme: 'djangocfg-light',
    base00: 'transparent',
    base01: '#f3f4f6',
    base02: '#e5e7eb',
    base03: '#9ca3af',
    base04: '#6b7280',
    base05: '#1f2937',
    base06: '#111827',
    base07: '#000000',
    base08: '#dc2626', // null, undefined
    base09: '#c2410c', // numbers
    base0A: '#b45309', // strings
    base0B: '#15803d', // booleans
    base0C: '#0e7490', // dates, regex
    base0D: '#1d4ed8', // keys
    base0E: '#7e22ce', // functions
    base0F: '#be123c', // deprecations
  },
};

const TRUNCATION_SUFFIX = '…';

interface JsonContentProps {
  data: unknown;
  renderKey: number;
  title?: string;
  showTitle: boolean;
  padding: string;
  collectionLimit: number;
  preserveKeyOrder: boolean;
  showCollectionInfo: boolean;
  maxStringLength: number;
  fontSize?: string;
  shouldExpandNodeInitially: (keyPath: readonly (string | number)[], nodeData: unknown, level: number) => boolean;
  jsonTreeProps?: Partial<CommonExternalProps>;
}

/**
 * JsonContent — react-json-tree wrapper with theming.
 *
 * Memoised: re-renders only when any prop changes. `data` is compared
 * by reference — the parent should not mutate the object in place.
 * Light/dark palette follows the host theme via `useResolvedTheme`.
 */
const JsonContent = memo(({
  data,
  renderKey,
  title,
  showTitle,
  padding,
  collectionLimit,
  preserveKeyOrder,
  showCollectionInfo,
  maxStringLength,
  fontSize,
  shouldExpandNodeInitially,
  jsonTreeProps = {},
}: JsonContentProps) => {
  const resolvedTheme = useResolvedTheme();
  const palette = resolvedTheme === 'dark' ? DARK_PALETTE : LIGHT_PALETTE;

  const getItemString = showCollectionInfo
    ? (nodeType: string, nodeData: unknown) => {
        if (nodeType === 'Array') {
          const length = Array.isArray(nodeData) ? nodeData.length : 0;
          return length > 0 ? <span className="text-xs" style={{ color: palette.muted }}>({length})</span> : null;
        }
        if (nodeType === 'Object') {
          const keys = nodeData && typeof nodeData === 'object' ? Object.keys(nodeData) : [];
          return keys.length > 0 ? <span className="text-xs" style={{ color: palette.muted }}>{`{${keys.length}}`}</span> : null;
        }
        return null;
      }
    : () => null;

  const isUrl = (value: unknown): value is string =>
    typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://'));

  // Truncation lives in valueRenderer (not postprocessValue) so the full
  // string stays intact for copy/download and is still reachable via the
  // `title` tooltip. URLs render as clickable links.
  const valueRenderer = (displayValue: unknown, rawValue: unknown) => {
    if (isUrl(rawValue)) {
      const truncated =
        rawValue.length > maxStringLength
          ? rawValue.slice(0, maxStringLength) + TRUNCATION_SUFFIX
          : rawValue;
      return (
        <a
          href={rawValue}
          target="_blank"
          rel="noopener noreferrer"
          title={rawValue}
          style={{ color: palette.string, textDecoration: 'underline' }}
          onClick={(e) => e.stopPropagation()}
        >
          {`"${truncated}"`}
        </a>
      );
    }
    if (typeof rawValue === 'string' && rawValue.length > maxStringLength) {
      return (
        <span title={rawValue}>
          {`"${rawValue.slice(0, maxStringLength)}${TRUNCATION_SUFFIX}"`}
        </span>
      );
    }
    return displayValue as React.ReactNode;
  };

  return (
    <div
      role="region"
      aria-label={title ? `JSON: ${title}` : 'JSON data'}
      className={`overflow-auto h-full rounded-md border border-border/60 ${padding}`}
      style={{
        backgroundColor: palette.surface,
        color: palette.fg,
        ...(fontSize ? { fontSize } : null),
      }}
    >
      {showTitle && title && (
        <h6 className="text-sm font-semibold mb-2" style={{ color: palette.title }}>{title}</h6>
      )}
      <JSONTree
        key={renderKey}
        data={data}
        theme={palette.theme}
        invertTheme={false}
        hideRoot={true}
        shouldExpandNodeInitially={shouldExpandNodeInitially}
        getItemString={getItemString}
        valueRenderer={valueRenderer}
        collectionLimit={collectionLimit}
        sortObjectKeys={!preserveKeyOrder}
        {...jsonTreeProps}
      />
    </div>
  );
});

export { JsonContent };
export default JsonContent;
