/* controls.jsx — UI Primitives */
const { useRef, useState } = React;

const IC = {
  plus: 'M12 5v14M5 12h14',
  trash: 'M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6',
  eye: ['M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z','M12 9a3 3 0 1 0 0 6 3 3 0 0 0 0-6z'],
  eyeOff: ['M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94','M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19','M1 1l22 22'],
  download: ['M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4','M7 10l5 5 5-5','M12 15V3'],
  copy: ['M20 9H11a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2z','M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 0 2 2v1'],
  reset: 'M1 4v6h6M23 20v-6h-6M20.49 9A9 9 0 0 0 5.64 5.64L1 10M23 14l-4.64 4.36A9 9 0 0 1 3.51 15',
  cube: ['M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z','M3.27 6.96L12 12.01l8.73-5.05','M12 22.08V12'],
  layers: ['M12 2L2 7l10 5 10-5-10-5z','M2 17l10 5 10-5','M2 12l10 5 10-5'],
  move: ['M5 9l-3 3 3 3','M9 5l3-3 3 3','M15 19l-3 3-3-3','M19 9l3 3-3 3','M2 12h20','M12 2v20'],
  rotate: ['M23 4v6h-6','M1 20v-6h6','M3.51 9a9 9 0 0 1 14.85-3.36L23 10','M1 14l4.64 4.36A9 9 0 0 0 20.49 15'],
  scale: ['M21 3l-6 6','M3 21l6-6','M21 21l-6-6','M3 3l6 6','M15 3h6v6','M3 15v6h6'],
  sparkle: 'M12 3L9.5 9.5 3 12l6.5 2.5L12 21l2.5-6.5L21 12l-6.5-2.5z',
  check: 'M20 6L9 17l-5-5',
  close: 'M18 6L6 18M6 6l12 12',
  upload: ['M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4','M17 8l-5-5-5 5','M12 3v12'],
  sun: ['M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42','M12 17a5 5 0 1 0 0-10 5 5 0 0 0 0 10z'],
  image: ['M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z','M8.5 8.5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z','M21 15l-5-5L5 21'],
  grid: ['M3 3h7v7H3z','M14 3h7v7h-7z','M14 14h7v7h-7z','M3 14h7v7H3z'],
  dice: ['M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z'],
  pencil: ['M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7','M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z'],
  focus: ['M8 3H5a2 2 0 0 0-2 2v3','M21 8V5a2 2 0 0 0-2-2h-3','M3 16v3a2 2 0 0 0 2 2h3','M16 21h3a2 2 0 0 0 2-2v-3'],
  palette: ['M12 2a10 10 0 1 0 0 20','M12 2c-2.76 0-5 4.48-5 10s2.24 10 5 10','M2 12h20'],
};

const Svg = ({ d, size = 14, sw = 1.5, fill = 'none', stroke = 'currentColor' }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke={stroke}
    strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
    {(Array.isArray(d) ? d : [d]).map((p, i) => <path key={i} d={p} />)}
  </svg>
);

const Row = ({ label, children, style }) => (
  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 7, ...style }}>
    {label && <span style={{ fontSize: 11, color: 'var(--t2)', width: 82, flexShrink: 0 }}>{label}</span>}
    {children}
  </div>
);

const Slider = ({ label, value, min = 0, max = 1, step = 0.01, onChange }) => (
  <div style={{ marginBottom: 9 }}>
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 4 }}>
      <span style={{ fontSize: 11, color: 'var(--t2)' }}>{label}</span>
      <span style={{ fontSize: 10, color: 'var(--t3)', fontVariantNumeric: 'tabular-nums', minWidth: 32, textAlign: 'right' }}>
        {typeof value === 'number' ? (step < 0.1 ? value.toFixed(2) : value.toFixed(1)) : value}
      </span>
    </div>
    <input type="range" min={min} max={max} step={step} value={value} onChange={e => onChange(parseFloat(e.target.value))} />
  </div>
);

const ColorRow = ({ label, value, onChange }) => (
  <Row label={label}>
    <input type="color" value={value} onChange={e => onChange(e.target.value)} />
    <input type="text" value={value} onChange={e => onChange(e.target.value)}
      style={{ flex: 1, fontFamily: 'monospace', fontSize: 10.5 }} />
    <button onClick={() => { const h = Math.floor(Math.random()*360); onChange(`hsl(${h},70%,60%)`); }}
      title="Случайный" style={{ background:'none',border:'none',padding:'2px 3px',cursor:'pointer',color:'var(--t3)',display:'flex',borderRadius:4 }}>
      <Svg d={IC.dice} size={12} />
    </button>
  </Row>
);

const NumInput = ({ value, onChange, min, max, step = 0.1, style }) => (
  <input type="number" value={typeof value === 'number' ? parseFloat(value.toFixed(3)) : value}
    min={min} max={max} step={step}
    onChange={e => { const v = parseFloat(e.target.value); if (!isNaN(v)) onChange(v); }}
    style={{ width: '100%', textAlign: 'center', ...style }} />
);

const Toggle = ({ label, value, onChange }) => (
  <Row label={label}>
    <button onClick={() => onChange(!value)} style={{
      background: value ? 'var(--accent)' : 'var(--bg3)',
      border: `1px solid ${value ? 'var(--accent)' : 'var(--border)'}`,
      borderRadius: 20, width: 34, height: 18, padding: 0,
      position: 'relative', cursor: 'pointer', transition: 'all .2s', flexShrink: 0,
    }}>
      <div style={{ position: 'absolute', top: 2, left: value ? 16 : 2,
        width: 12, height: 12, borderRadius: '50%', background: '#fff', transition: 'left .2s' }} />
    </button>
  </Row>
);

const Btn = ({ children, onClick, variant = 'ghost', size = 'sm', style, title, disabled }) => {
  const V = { ghost:{bg:'transparent',bo:'transparent',co:'var(--t1)'}, outline:{bg:'transparent',bo:'var(--border)',co:'var(--t1)'}, solid:{bg:'var(--accent)',bo:'var(--accent)',co:'#fff'}, danger:{bg:'transparent',bo:'transparent',co:'#ff453a'}, subtle:{bg:'var(--bg3)',bo:'var(--border)',co:'var(--t1)'} };
  const S = { xs:{p:'2px 6px',fs:10}, sm:{p:'4px 9px',fs:11}, md:{p:'6px 14px',fs:12} };
  const v = V[variant]||V.ghost; const s = S[size]||S.sm;
  return (
    <button onClick={onClick} title={title} disabled={disabled} style={{
      background: v.bg, border: `1px solid ${v.bo}`, color: v.co, padding: s.p, fontSize: s.fs,
      borderRadius: 6, cursor: disabled ? 'not-allowed' : 'pointer',
      display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: 'inherit',
      opacity: disabled ? 0.4 : 1, transition: 'background .12s, border-color .12s', ...style,
    }}
      onMouseEnter={e => { if (!disabled) e.currentTarget.style.background = variant==='solid'?'#7b79ff':'var(--bg3)'; }}
      onMouseLeave={e => { e.currentTarget.style.background = v.bg; }}>
      {children}
    </button>
  );
};

const Divider = () => <div style={{ height: 1, background: 'var(--border)', margin: '12px 0' }} />;

const SectionHead = ({ children }) => (
  <div style={{ fontSize: 9.5, color: 'var(--t3)', fontWeight: 600, letterSpacing: '0.1em',
    textTransform: 'uppercase', marginBottom: 8, marginTop: 4 }}>{children}</div>
);

const XYZRow = ({ label, vx, vy, vz, onChange, step = 0.1 }) => (
  <div style={{ marginBottom: 8 }}>
    {label && <div style={{ fontSize: 11, color: 'var(--t2)', marginBottom: 4 }}>{label}</div>}
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 4 }}>
      {['x','y','z'].map((ax, i) => (
        <div key={ax} style={{ position: 'relative' }}>
          <span style={{ position:'absolute',left:6,top:'50%',transform:'translateY(-50%)',
            fontSize:9,color:'var(--t3)',fontWeight:600,pointerEvents:'none',zIndex:1 }}>
            {ax.toUpperCase()}
          </span>
          <NumInput value={[vx,vy,vz][i]} step={step}
            onChange={v => onChange(ax==='x'?{x:v}:ax==='y'?{y:v}:{z:v})}
            style={{ paddingLeft:16,fontSize:11 }} />
        </div>
      ))}
    </div>
  </div>
);

const EmptyState = ({ label }) => (
  <div style={{ padding:'20px 6px',textAlign:'center',color:'var(--t3)',fontSize:11,lineHeight:1.7 }}>{label}</div>
);

const TextureSlot = ({ label, type, objectId, engineRef }) => {
  const inputRef = useRef(null);
  const [has, setHas] = useState(false);
  const upload = f => {
    if (!f || !objectId || !engineRef.current) return;
    engineRef.current.loadTexture(URL.createObjectURL(f), type, objectId);
    setHas(true);
  };
  const clear = () => { engineRef.current?.clearTexture(type, objectId); setHas(false); };
  return (
    <div style={{ display:'flex',alignItems:'center',gap:7,marginBottom:7 }}>
      <div onClick={() => inputRef.current?.click()} style={{
        width:36,height:28,background:has?'rgba(94,92,230,.2)':'var(--bg3)',
        border:`1px ${has?'solid var(--accent)':'dashed var(--border)'}`,
        borderRadius:5,cursor:'pointer',display:'flex',alignItems:'center',justifyContent:'center',
        transition:'all .15s',
      }}>
        <Svg d={has?IC.check:IC.plus} size={11} stroke={has?'var(--accent)':'var(--t3)'} />
      </div>
      <span style={{ flex:1,fontSize:11,color:has?'var(--t1)':'var(--t2)' }}>{label}</span>
      {has && <button onClick={clear} style={{background:'none',border:'none',padding:2,cursor:'pointer',color:'#ff453a',display:'flex'}}><Svg d={IC.close} size={11}/></button>}
      <input ref={inputRef} type="file" accept="image/*" style={{display:'none'}}
        onChange={e => e.target.files[0] && upload(e.target.files[0])} />
    </div>
  );
};

Object.assign(window, { IC, Svg, Row, Slider, ColorRow, NumInput, Toggle, Btn, Divider, SectionHead, XYZRow, EmptyState, TextureSlot });
