'use client';

import { useRef, useState } from 'react';

export type JsonValue =
  | string
  | number
  | boolean
  | null
  | JsonValue[]
  | { [k: string]: JsonValue };

const IMAGE_KEYS = new Set(['cover', 'image', 'avatar', 'gallery', 'logo', 'icon']);
const IMAGE_RE = /\.(png|jpe?g|svg|webp|gif|avif)$/i;

function looksLikeImage(key: string | undefined, value: string): boolean {
  if (key && IMAGE_KEYS.has(key)) return true;
  return IMAGE_RE.test(value) || value.startsWith('/api/uploads/') || value.startsWith('/portfolio/');
}

/** Produce an "empty" clone of a sample value, for new array entries. */
function emptyLike(sample: JsonValue | undefined): JsonValue {
  if (sample === undefined || sample === null) return '';
  if (typeof sample === 'string') return '';
  if (typeof sample === 'number') return 0;
  if (typeof sample === 'boolean') return false;
  if (Array.isArray(sample)) return [];
  const out: Record<string, JsonValue> = {};
  for (const [k, v] of Object.entries(sample)) out[k] = emptyLike(v);
  return out;
}

function humanize(key: string): string {
  return key
    .replace(/[_-]/g, ' ')
    .replace(/([a-z])([A-Z])/g, '$1 $2')
    .replace(/^\w/, (c) => c.toUpperCase());
}

export function JsonForm({
  value,
  onChange,
  fieldKey,
  depth = 0,
}: {
  value: JsonValue;
  onChange: (v: JsonValue) => void;
  fieldKey?: string;
  depth?: number;
}) {
  // ---- Primitive: string ----
  if (typeof value === 'string') {
    if (looksLikeImage(fieldKey, value)) {
      return <ImageInput value={value} onChange={onChange} />;
    }
    const multiline = value.length > 64 || value.includes('\n');
    return multiline ? (
      <textarea
        value={value}
        onChange={(e) => onChange(e.target.value)}
        rows={Math.min(8, Math.max(2, Math.ceil(value.length / 60)))}
        className="w-full resize-y rounded-lg border border-white/10 bg-base-900/60 px-3 py-2 text-sm text-slate-100 outline-none focus:border-electric/60"
      />
    ) : (
      <input
        type="text"
        value={value}
        onChange={(e) => onChange(e.target.value)}
        className="w-full rounded-lg border border-white/10 bg-base-900/60 px-3 py-2 text-sm text-slate-100 outline-none focus:border-electric/60"
      />
    );
  }

  // ---- Primitive: number ----
  if (typeof value === 'number') {
    return (
      <input
        type="number"
        value={value}
        onChange={(e) => onChange(e.target.value === '' ? 0 : Number(e.target.value))}
        className="w-40 rounded-lg border border-white/10 bg-base-900/60 px-3 py-2 text-sm text-slate-100 outline-none focus:border-electric/60"
      />
    );
  }

  // ---- Primitive: boolean ----
  if (typeof value === 'boolean') {
    return (
      <label className="inline-flex cursor-pointer items-center gap-2 text-sm text-slate-300">
        <input
          type="checkbox"
          checked={value}
          onChange={(e) => onChange(e.target.checked)}
          className="h-4 w-4 accent-electric"
        />
        {value ? 'true' : 'false'}
      </label>
    );
  }

  // ---- null ----
  if (value === null) {
    return (
      <input
        type="text"
        value=""
        placeholder="(empty)"
        onChange={(e) => onChange(e.target.value)}
        className="w-full rounded-lg border border-white/10 bg-base-900/60 px-3 py-2 text-sm text-slate-100 outline-none focus:border-electric/60"
      />
    );
  }

  // ---- Array ----
  if (Array.isArray(value)) {
    return (
      <div className="flex flex-col gap-3">
        {value.map((item, i) => (
          <div
            key={i}
            className="rounded-xl border border-white/8 bg-white/[0.015] p-3"
          >
            <div className="mb-2 flex items-center justify-between">
              <span className="font-mono text-[0.65rem] uppercase tracking-wider text-slate-500">
                {fieldKey ? humanize(fieldKey).replace(/s$/, '') : 'Item'} #{i + 1}
              </span>
              <div className="flex gap-1">
                {i > 0 && (
                  <MiniBtn label="↑" title="Move up" onClick={() => {
                    const cp = [...value];
                    [cp[i - 1], cp[i]] = [cp[i], cp[i - 1]];
                    onChange(cp);
                  }} />
                )}
                {i < value.length - 1 && (
                  <MiniBtn label="↓" title="Move down" onClick={() => {
                    const cp = [...value];
                    [cp[i + 1], cp[i]] = [cp[i], cp[i + 1]];
                    onChange(cp);
                  }} />
                )}
                <MiniBtn
                  label="✕"
                  title="Remove"
                  danger
                  onClick={() => onChange(value.filter((_, j) => j !== i))}
                />
              </div>
            </div>
            <JsonForm
              value={item}
              fieldKey={fieldKey}
              depth={depth + 1}
              onChange={(nv) => {
                const cp = [...value];
                cp[i] = nv;
                onChange(cp);
              }}
            />
          </div>
        ))}
        <button
          type="button"
          onClick={() => onChange([...value, emptyLike(value[0])])}
          className="self-start rounded-lg border border-electric/30 bg-electric/5 px-3 py-1.5 text-xs font-medium text-electric transition-colors hover:bg-electric/10"
        >
          + Add {fieldKey ? humanize(fieldKey).replace(/s$/, '').toLowerCase() : 'item'}
        </button>
      </div>
    );
  }

  // ---- Object ----
  return (
    <div
      className={
        depth === 0
          ? 'flex flex-col gap-5'
          : 'flex flex-col gap-4 rounded-xl border-s-2 border-white/10 ps-4'
      }
    >
      {Object.entries(value).map(([k, v]) => {
        const primitive =
          v === null || ['string', 'number', 'boolean'].includes(typeof v);
        return (
          <div
            key={k}
            className={primitive ? 'flex flex-col gap-1.5' : 'flex flex-col gap-2'}
          >
            <label className="font-mono text-[0.68rem] uppercase tracking-wider text-slate-400">
              {humanize(k)}
            </label>
            <JsonForm
              value={v}
              fieldKey={k}
              depth={depth + 1}
              onChange={(nv) => onChange({ ...value, [k]: nv })}
            />
          </div>
        );
      })}
    </div>
  );
}

function MiniBtn({
  label,
  title,
  onClick,
  danger,
}: {
  label: string;
  title: string;
  onClick: () => void;
  danger?: boolean;
}) {
  return (
    <button
      type="button"
      title={title}
      onClick={onClick}
      className={
        'inline-flex h-6 w-6 items-center justify-center rounded-md border text-xs transition-colors ' +
        (danger
          ? 'border-magenta/30 text-magenta hover:bg-magenta/10'
          : 'border-white/10 text-slate-400 hover:bg-white/[0.06] hover:text-white')
      }
    >
      {label}
    </button>
  );
}

function ImageInput({
  value,
  onChange,
}: {
  value: string;
  onChange: (v: string) => void;
}) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [uploading, setUploading] = useState(false);
  const [err, setErr] = useState<string | null>(null);

  async function upload(file: File) {
    setUploading(true);
    setErr(null);
    try {
      const fd = new FormData();
      fd.append('file', file);
      const res = await fetch('/api/admin/upload', { method: 'POST', body: fd });
      if (!res.ok) throw new Error(`upload failed (${res.status})`);
      const json = await res.json();
      if (json?.url) onChange(json.url as string);
      else throw new Error('no url returned');
    } catch (e) {
      setErr(e instanceof Error ? e.message : 'upload error');
    } finally {
      setUploading(false);
    }
  }

  return (
    <div className="flex items-start gap-3">
      <div className="relative h-16 w-24 shrink-0 overflow-hidden rounded-lg border border-white/10 bg-base-900/60">
        {value ? (
          // eslint-disable-next-line @next/next/no-img-element
          <img src={value} alt="" className="h-full w-full object-cover" />
        ) : (
          <span className="flex h-full w-full items-center justify-center text-[0.6rem] text-slate-600">
            no image
          </span>
        )}
      </div>
      <div className="flex grow flex-col gap-2">
        <input
          type="text"
          value={value}
          onChange={(e) => onChange(e.target.value)}
          placeholder="/api/uploads/… or /portfolio/…"
          className="w-full rounded-lg border border-white/10 bg-base-900/60 px-3 py-2 text-sm text-slate-100 outline-none focus:border-electric/60"
        />
        <div className="flex items-center gap-2">
          <button
            type="button"
            onClick={() => inputRef.current?.click()}
            disabled={uploading}
            className="rounded-lg border border-white/10 bg-white/[0.04] px-3 py-1.5 text-xs text-slate-200 transition-colors hover:bg-white/[0.08] disabled:opacity-50"
          >
            {uploading ? 'Uploading…' : 'Upload image'}
          </button>
          {err && <span className="text-xs text-magenta">{err}</span>}
        </div>
        <input
          ref={inputRef}
          type="file"
          accept="image/*"
          className="hidden"
          onChange={(e) => {
            const f = e.target.files?.[0];
            if (f) upload(f);
            e.target.value = '';
          }}
        />
      </div>
    </div>
  );
}
