/** Shared React field tree for d4-filter-explorer + main index editor — load after React + Babel + d4-filter-shared.js */
const { useMemo, Fragment } = React;
const {
  COND_GROUP_NAMES, CG_OVERRIDES, fieldMeta,
  fixed32ToHex, fixed32ToU32LE, fixed32FromU32LE,
  rgbaCss, rgbaHex, hexToRgba,
} = window.D4Filter;

function wireLabel(wt) {
  return { 0: 'varint', 1: 'fixed64', 2: 'len-delim', 5: 'fixed32' }[wt] || ('wt' + wt);
}

function ValueSummary({ field, meta, knownIds, friendlyUI }) {
  if (field.kind === 'varint') {
    const v = Number(field.value);
    if (meta?.bitmask) {
      const set = meta.bitmask.filter((_, i) => (v >> i) & 1);
      return friendlyUI
        ? <span className="text-sm text-stone-300">{set.join(', ') || 'None selected'}</span>
        : <span className="mono">{v} <span className="text-slate-500">(0x{v.toString(16)})</span> = <span className="text-cyan-300">{set.join(' | ') || '∅'}</span></span>;
    }
    const enumLabel = meta?.enum?.[v];
    return friendlyUI && enumLabel
      ? <span className="text-sm text-stone-300">{enumLabel}</span>
      : <span className="mono">{String(v)}{enumLabel ? <span className="text-cyan-300"> ({enumLabel})</span> : null}</span>;
  }
  if (field.kind === 'string') return friendlyUI ? <span className="text-sm text-amber-100/90">"{field.value}"</span> : <span className="text-emerald-300 mono">"{field.value}"</span>;
  if (field.kind === 'fixed32') {
    const u32 = fixed32ToU32LE(field.value);
    if (meta?.kind === 'color') {
      return (
        <span className={`inline-flex items-center gap-2 ${friendlyUI ? 'text-sm' : 'mono'}`}>
          <span className="inline-block w-4 h-4 rounded border border-slate-600 shrink-0" style={{ background: rgbaCss(field.value) }}></span>
          {!friendlyUI && <>{rgbaHex(field.value)} α={field.value[3]}</>}
          {friendlyUI && <span className="text-stone-300">Tint preview</span>}
        </span>
      );
    }
    const known = knownIds[u32];
    return friendlyUI
      ? <span className="text-sm text-stone-300">{known || `ID ${u32}`}</span>
      : <span className="mono">{`0x${u32.toString(16).padStart(8, '0')}`} <span className="text-slate-500">({u32})</span>{known ? <span className="text-amber-300"> · {known}</span> : null}</span>;
  }
  if (field.kind === 'fixed64') return friendlyUI ? <span className="text-sm text-stone-500">Binary ID</span> : <span className="mono">{Array.from(field.value).map(b => b.toString(16).padStart(2, '0')).join('')}</span>;
  if (field.kind === 'bytes') return <span className={`text-stone-500 ${friendlyUI ? 'text-sm' : 'mono'}`}>{friendlyUI ? 'Custom data' : `${field.value.length} bytes`}</span>;
  if (field.kind === 'message') return <span className="text-stone-500 text-sm">{(field.children || []).length} nested {((field.children || []).length === 1) ? 'item' : 'items'}</span>;
  return null;
}

function FieldEditor({ field, meta, onChange, knownIds, setKnownIds, readOnly, friendlyUI }) {
  if (readOnly) return null;
  const inp = friendlyUI ? 'rounded-lg border border-stone-600/80 bg-stone-900/90 px-3 py-2 text-sm text-stone-100 placeholder:text-stone-500 focus:border-amber-500/50 focus:outline-none focus:ring-1 focus:ring-amber-500/30' : 'border border-slate-700 rounded px-2 py-0.5 text-xs mono';
  if (field.kind === 'varint') {
    if (meta?.bitmask) {
      const v = Number(field.value);
      return (
        <div className={`flex flex-wrap items-center gap-x-4 gap-y-2 py-2 ${friendlyUI ? '' : ''}`}>
          {!friendlyUI && <span className="text-xs text-slate-500 w-20 shrink-0">bitmask</span>}
          {meta.bitmask.map((label, i) => (
            <label key={i} className={`flex items-center gap-2 ${friendlyUI ? 'text-sm text-stone-200' : 'text-xs gap-1'}`}>
              <input type="checkbox" className={friendlyUI ? 'rounded border-stone-500' : ''} checked={!!((v >> i) & 1)}
                onChange={e => {
                  let nv = v;
                  if (e.target.checked) nv |= (1 << i); else nv &= ~(1 << i);
                  onChange(f => ({ ...f, value: nv }));
                }} />
              {label}{!friendlyUI && <span className="text-slate-500 mono">({1 << i})</span>}
            </label>
          ))}
          {!friendlyUI && (
            <Fragment>
              <input type="number" value={v} onChange={e => onChange(f => ({ ...f, value: Number(e.target.value) || 0 }))}
                className="border border-slate-700 rounded px-2 py-0.5 text-xs mono w-20" />
              <span className="text-xs text-slate-500">0x{v.toString(16)}</span>
            </Fragment>
          )}
        </div>
      );
    }
    return (
      <div className="flex flex-col gap-1 py-2 sm:flex-row sm:items-center sm:gap-3">
        {!friendlyUI && <span className="text-xs text-slate-500 w-20 shrink-0">varint</span>}
        {meta?.enum ? (
          <select value={Number(field.value)} onChange={e => onChange(f => ({ ...f, value: Number(e.target.value) }))}
            className={friendlyUI ? `${inp} w-full max-w-md` : 'border border-slate-700 rounded px-2 py-0.5 text-xs mono'}>
            {Object.entries(meta.enum).map(([k, v]) => (
              <option key={k} value={k}>{friendlyUI ? v : `${k} — ${v}`}</option>
            ))}
            {!meta.enum[Number(field.value)] && (
              <option value={Number(field.value)}>{Number(field.value)} — (unknown)</option>
            )}
          </select>
        ) : (
          <input type="number" value={String(field.value)} onChange={e => {
            const v = e.target.value === '' ? 0 : Number(e.target.value);
            onChange(f => ({ ...f, value: v }));
          }} className={friendlyUI ? `${inp} max-w-[12rem]` : 'border border-slate-700 rounded px-2 py-0.5 text-xs mono w-32'} />
        )}
        {!friendlyUI && meta?.enum && <span className="text-xs text-slate-500">raw value: {String(field.value)}</span>}
      </div>
    );
  }
  if (field.kind === 'string') {
    return (
      <div className={`flex gap-2 py-2 ${friendlyUI ? 'flex-col' : 'items-center py-1'}`}>
        {!friendlyUI && <span className="text-xs text-slate-500 w-20">string</span>}
        <input type="text" value={field.value} onChange={e => onChange(f => ({ ...f, value: e.target.value }))}
          className={friendlyUI ? `${inp} w-full` : 'border border-slate-700 rounded px-2 py-0.5 text-xs mono w-full'} />
      </div>
    );
  }
  if (field.kind === 'fixed32') {
    const u32 = fixed32ToU32LE(field.value);
    const isColor = meta?.kind === 'color';
    return (
      <div className="flex flex-col gap-3 py-2">
        {!friendlyUI && (
          <div className="flex flex-wrap items-center gap-2">
            <span className="text-xs text-slate-500 w-20">fixed32</span>
            <span className="text-xs text-slate-400">wire bytes:</span>
            <input type="text" value={fixed32ToHex(field.value)}
              onChange={e => {
                const parts = e.target.value.trim().split(/\s+/).slice(0, 4).map(x => parseInt(x, 16) || 0);
                const arr = new Uint8Array(4);
                for (let i = 0; i < 4; i++) arr[i] = parts[i] || 0;
                onChange(f => ({ ...f, value: arr }));
              }}
              className="border border-slate-700 rounded px-2 py-0.5 text-xs mono w-32" />
            <span className="text-xs text-slate-400">u32 LE:</span>
            <input type="number" value={u32}
              onChange={e => {
                const n = Number(e.target.value) >>> 0;
                onChange(f => ({ ...f, value: fixed32FromU32LE(n) }));
              }}
              className="border border-slate-700 rounded px-2 py-0.5 text-xs mono w-32" />
          </div>
        )}
        {friendlyUI && !isColor && (
          <div className="flex flex-wrap items-center gap-2">
            <label className="text-sm text-stone-400 whitespace-nowrap">Game ID</label>
            <input type="number" value={u32}
              onChange={e => {
                const n = Number(e.target.value) >>> 0;
                onChange(f => ({ ...f, value: fixed32FromU32LE(n) }));
              }}
              className={`${inp} w-36 font-mono text-sm`} />
          </div>
        )}
        {isColor && (
          <div className="flex flex-wrap items-center gap-3">
            {friendlyUI && <span className="text-sm text-stone-400">Color</span>}
            <input type="color" value={rgbaHex(field.value)}
              onChange={e => {
                const arr = hexToRgba(e.target.value, field.value[3]);
                if (arr) onChange(f => ({ ...f, value: arr }));
              }}
              className="h-9 w-14 cursor-pointer rounded border border-stone-600 bg-stone-900"
            />
            <span className={`${friendlyUI ? 'text-sm text-stone-400' : 'text-xs text-slate-400'}`.trim()}>{friendlyUI ? 'Opacity' : 'α:'}</span>
            <input type="number" min="0" max="255" value={field.value[3]}
              onChange={e => {
                const v = Math.max(0, Math.min(255, Number(e.target.value)));
                const arr = new Uint8Array(field.value); arr[3] = v;
                onChange(f => ({ ...f, value: arr }));
              }}
              className={friendlyUI ? `${inp} w-20` : 'border border-slate-700 rounded px-2 py-0.5 text-xs mono w-16'} />
          </div>
        )}
        {!isColor && (
          <div className={`flex flex-col gap-1 ${friendlyUI ? 'sm:flex-row sm:items-center sm:gap-3' : 'flex-row items-center gap-1'}`}>
            <span className={`${friendlyUI ? 'text-sm text-stone-400 shrink-0' : 'text-xs text-slate-400'}`}>{friendlyUI ? 'Your label (optional)' : 'label this ID:'}</span>
            <input type="text" placeholder={friendlyUI ? 'e.g. Ring of intrigue' : 'affix name…'} value={knownIds[u32] || ''}
              onChange={e => {
                const next = { ...knownIds };
                if (e.target.value) next[u32] = e.target.value; else delete next[u32];
                setKnownIds(next);
              }}
              className={friendlyUI ? `${inp} flex-1 min-w-0 max-w-md` : 'border border-slate-700 rounded px-2 py-0.5 text-xs mono w-48'} />
          </div>
        )}
      </div>
    );
  }
  if (field.kind === 'bytes') {
    return (
      <div className={`flex gap-2 py-2 ${friendlyUI ? 'flex-col' : 'items-start py-1'}`}>
        <span className={`${friendlyUI ? 'text-sm text-stone-400' : 'text-xs text-slate-500 w-20'} shrink-0`}>{friendlyUI ? 'Raw hex (advanced)' : 'bytes'}</span>
        <textarea value={Array.from(field.value).map(b => b.toString(16).padStart(2, '0')).join(' ')}
          onChange={e => {
            const parts = e.target.value.trim().split(/\s+/).map(x => parseInt(x, 16) || 0);
            onChange(f => ({ ...f, value: new Uint8Array(parts) }));
          }}
          className={friendlyUI ? `${inp} w-full font-mono text-xs h-20` : 'border border-slate-700 rounded px-2 py-0.5 text-xs mono w-full h-16'} />
      </div>
    );
  }
  return null;
}

function FieldList(props) {
  const {
    fields, basePath, schemaPath, update, del, dup, selected, setSelected, annotate,
    knownIds, setKnownIds,
    lockConditionGroupType = false,
    compact = false,
    friendlyUI = false,
    omitFieldNums = null,
  } = props;
  const parentKey = schemaPath.join('.');
  const condGroupType = parentKey === '1.4'
    ? fields.find(f => f.fieldNum === 1 && f.kind === 'varint')?.value
    : null;
  return (
    <ol className={friendlyUI ? 'list-none space-y-2 pl-0 [&>li]:list-none' : 'space-y-1'}>
      {fields.map((f, i) => {
        if (omitFieldNums && omitFieldNums.includes(f.fieldNum)) return null;
        return (
          <FieldNode key={i} field={f} index={i}
            basePath={[...basePath, i]}
            schemaPath={[...schemaPath, f.fieldNum]}
            condGroupType={condGroupType}
            update={update} del={del} dup={dup}
            selected={selected} setSelected={setSelected}
            annotate={annotate}
            knownIds={knownIds} setKnownIds={setKnownIds}
            lockConditionGroupType={lockConditionGroupType}
            compact={compact}
            friendlyUI={friendlyUI}
            omitFieldNums={omitFieldNums}
          />
        );
      })}
    </ol>
  );
}

function FieldNode({
  field, basePath, schemaPath, update, del, dup, selected, setSelected, annotate, knownIds, setKnownIds, condGroupType,
  lockConditionGroupType,
  compact,
  friendlyUI,
  omitFieldNums,
}) {
  const path = basePath;
  const isSelected = selected && selected.join('.') === path.join('.');
  const parentSchemaPath = schemaPath.slice(0, -1);
  const baseMeta = fieldMeta(parentSchemaPath, field.fieldNum);
  const meta = useMemo(() => {
    if (parentSchemaPath.join('.') !== '1.4' || condGroupType == null) return baseMeta;
    const ov = CG_OVERRIDES[field.fieldNum]?.[Number(condGroupType)];
    if (!ov) return baseMeta;
    return { ...(baseMeta || {}), ...(ov.label ? { name: ov.label } : {}), ...(ov.bitmask ? { bitmask: ov.bitmask } : {}), ...(ov.enum ? { enum: ov.enum } : {}), ...(ov.idNamespace ? { idNamespace: ov.idNamespace } : {}) };
  }, [baseMeta, condGroupType, field.fieldNum, parentSchemaPath]);
  const labelName = annotate && meta?.name ? meta.name : (friendlyUI ? `Setting ${field.fieldNum}` : `field ${field.fieldNum}`);

  const innerName = useMemo(() => {
    if (field.kind !== 'message') return null;
    const nameField = (field.children || []).find(c => c.fieldNum === 1 && c.kind === 'string');
    if (nameField) return nameField.value;
    if (schemaPath.join('.') === '1.4') {
      const tField = (field.children || []).find(c => c.fieldNum === 1 && c.kind === 'varint');
      if (tField !== undefined) return COND_GROUP_NAMES[Number(tField.value)] || `(unknown type ${tField.value})`;
    }
    return null;
  }, [field, schemaPath]);

  const lockTypeField = lockConditionGroupType && field.fieldNum === 1 && field.kind === 'varint';
  const headerClick = (e) => {
    e.stopPropagation();
    if (setSelected) setSelected(path);
  };

  const sumCls = friendlyUI
    ? 'flex flex-wrap items-center gap-2 px-3 py-2 rounded-lg border border-stone-700/50 bg-stone-900/40'
    : 'flex items-start gap-2 px-2 py-1';

  return (
    <li className={`rounded-xl ${friendlyUI ? 'mb-2' : ''} ${isSelected ? 'selected' : friendlyUI ? '' : 'field-row'}`.trim()}>
      <details open={path.length < 2}>
        <summary className={sumCls} onClick={headerClick}>
          {!compact && !friendlyUI && <span className="mono text-xs text-slate-500 w-10 shrink-0">{path.join('.')}</span>}
          {!friendlyUI && (
            <span className="mono text-xs px-1 rounded bg-slate-800 text-slate-300 shrink-0">#{field.fieldNum} {wireLabel(field.wireType)}</span>
          )}
          <span className={`shrink-0 ${friendlyUI ? 'text-[15px] font-medium text-stone-100' : 'text-sm'}`}>{labelName}</span>
          {innerName && <span className={`shrink-0 ${friendlyUI ? 'text-xs text-stone-500' : 'text-sm text-emerald-300'}`}>{friendlyUI ? `(${innerName})` : `· ${innerName}`}</span>}
          <span className={`grow truncate min-w-[8rem] ${friendlyUI ? 'text-sm ml-1' : 'text-sm text-slate-400 ml-2'}`}>
            <ValueSummary field={field} meta={meta} knownIds={knownIds} friendlyUI={friendlyUI} />
          </span>
          {!lockTypeField && (
            <span className={`ml-auto flex shrink-0 items-center gap-1.5 ${friendlyUI ? '' : ''}`}>
              <button type="button" onClick={e => { e.stopPropagation(); dup(path); }}
                className={friendlyUI ? 'rounded-md bg-stone-700/90 px-2.5 py-1 text-xs font-medium text-stone-200 hover:bg-stone-600' : 'text-xs px-1 rounded bg-slate-800 hover:bg-slate-700'}>
                {friendlyUI ? 'Duplicate' : '+ dup'}
              </button>
              <button type="button" onClick={e => { e.stopPropagation(); del(path); }}
                className={friendlyUI ? 'rounded-md px-2.5 py-1 text-xs font-medium text-rose-300/90 hover:bg-rose-950/50' : 'text-xs px-1 rounded bg-rose-900 hover:bg-rose-800'}>
                {friendlyUI ? 'Remove' : 'delete'}
              </button>
            </span>
          )}
        </summary>
        <div className={friendlyUI ? 'mt-2 border-l-2 border-amber-600/35 pl-4 pr-2 pb-1' : 'pl-6 pr-2 pb-1'}>
          {lockTypeField && (
            <div className={`py-2 ${friendlyUI ? 'rounded-md bg-stone-900/60 px-3 text-sm text-stone-400' : 'text-xs text-slate-500 py-1'}`}>
              {friendlyUI ? 'You can change this rule’s condition type only by removing it and picking a different one from Add condition.' : 'Condition type is fixed (remove and re-add to change).'}
            </div>
          )}
          <FieldEditor field={field} meta={meta}
            onChange={(updater) => update(path, updater)}
            knownIds={knownIds} setKnownIds={setKnownIds}
            readOnly={lockTypeField}
            friendlyUI={friendlyUI}
          />
          {field.kind === 'message' && (
            <FieldList fields={field.children || []}
              basePath={path}
              schemaPath={schemaPath}
              update={update} del={del} dup={dup}
              selected={selected} setSelected={setSelected}
              annotate={annotate}
              knownIds={knownIds} setKnownIds={setKnownIds}
              lockConditionGroupType={false}
              compact={compact}
              friendlyUI={friendlyUI}
              omitFieldNums={omitFieldNums}
            />
          )}
        </div>
      </details>
    </li>
  );
}

function ConditionGroupInnerEditor({
  fields, basePath, schemaPath, update, del, dup, knownIds, setKnownIds,
  annotate = true,
  selected = null,
  setSelected = null,
  friendlyUI = true,
  omitFieldNums = null,
}) {
  return (
    <FieldList
      fields={fields || []}
      basePath={basePath}
      schemaPath={schemaPath}
      update={update}
      del={del}
      dup={dup}
      selected={selected}
      setSelected={setSelected}
      annotate={annotate}
      knownIds={knownIds}
      setKnownIds={setKnownIds}
      lockConditionGroupType
      compact={friendlyUI}
      friendlyUI={friendlyUI}
      omitFieldNums={omitFieldNums}
    />
  );
}

window.D4FilterFieldTree = {
  FieldList,
  FieldNode,
  FieldEditor,
  wireLabel,
  ValueSummary,
  ConditionGroupInnerEditor,
};
