import { useMemo, useState } from "react";
import { cn } from "@/lib/cn";

export interface JsonEditorProps {
  value: unknown;
  onChange: (value: unknown) => void;
  className?: string;
  readOnly?: boolean;
  rows?: number;
}

export function JsonEditor({ value, onChange, className, readOnly = false, rows = 16 }: JsonEditorProps) {
  const initialText = useMemo(() => safeStringify(value), [value]);
  const [text, setText] = useState<string>(initialText);
  const [error, setError] = useState<string | null>(null);

  // Re-sync external value changes if differs from current parsed text
  useMemo(() => {
    const external = safeStringify(value);
    if (external !== text && !error) {
      try {
        const parsed = JSON.parse(text);
        if (safeStringify(parsed) !== external) {
          setText(external);
        }
      } catch {
        // user is editing — keep text
      }
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [value]);

  const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
    const next = e.target.value;
    setText(next);
    if (readOnly) return;
    if (next.trim() === "") {
      setError(null);
      onChange(null);
      return;
    }
    try {
      const parsed = JSON.parse(next);
      setError(null);
      onChange(parsed);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Invalid JSON");
    }
  };

  return (
    <div className={cn("w-full", className)}>
      <textarea
        value={text}
        onChange={handleChange}
        readOnly={readOnly}
        rows={rows}
        spellCheck={false}
        className={cn(
          "input font-mono text-xs",
          error && "border-err focus:border-err focus:ring-err",
        )}
      />
      {error && <p className="mt-1 text-xs text-err">{error}</p>}
    </div>
  );
}

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