/* Remote mode — Part 3 (client UI).
 *
 * For now this file contains the LoginScreen ("CONNECT TO SAMPLER") overlay,
 * opened by clicking the BRAYER-SOUND logo. The actual control channel (the
 * Pi-side WebSocket server) is Part 2 and does not exist yet, so Connect
 * attempts a real socket and reports clearly when the sampler can't be reached.
 *
 * NOTE: all the React hooks (useState, useEffect, ...) are destructured once at
 * global scope in primitives.jsx — do NOT redeclare them here. Use React.useX.
 */

/* RemoteLink — owns the control-channel WebSocket: connect+login, promise-
 * correlated invoke, disconnect, and backoff auto-reconnect. One remote head. */
const RemoteLink = {
  ws: null,
  state: 'local',            // 'local' | 'connecting' | 'connected' | 'reconnecting'
  ip: null, username: null, password: null, token: null,
  port: 8420,
  _id: 1,
  _pending: {},              // id -> {resolve, reject}
  _listeners: [],
  _reconnectTimer: null,
  _reconnectAttempts: 0,
  _userClosed: false,
  onReconnected: null,       // optional callback set by the app (re-hydrate)
  hasRemoteClient: false,   // (Pi side) true while a Windows client is connected
  onSync: null,             // (Windows side) set by app: (cmd,args)=>applyCommandToState
  onSnapshot: null,         // (Windows side) set by app: (state)=>replace state
  onLibraryChanged: null,   // (Windows side) set by app: ()=>re-fetch soundmap list

  isConnected(){ return this.state === 'connected'; },

  onChange(fn){
    this._listeners.push(fn);
    return () => { this._listeners = this._listeners.filter(function(f){ return f !== fn; }); };
  },
  _emit(){
    const snap = { state: this.state, ip: this.ip };
    this._listeners.forEach(function(fn){ try { fn(snap); } catch(e){} });
  },

  connect(ip, username, password){
    this.ip = ip; this.username = username; this.password = password;
    this._userClosed = false; this._reconnectAttempts = 0;
    return this._open();
  },

  _open(){
    const self = this;
    return new Promise(function(resolve, reject){
      self.state = (self._reconnectAttempts > 0) ? 'reconnecting' : 'connecting';
      self._emit();
      let ws;
      try { ws = new WebSocket('ws://' + self.ip + ':' + self.port); }
      catch(e){ self.state = 'local'; self._emit(); reject(e); return; }
      self.ws = ws;
      let loggedIn = false;
      const to = setTimeout(function(){ if(!loggedIn){ try{ ws.close(); }catch(e){} reject(new Error('login timed out')); } }, 6000);
      ws.onopen = function(){
        ws.send(JSON.stringify({ type:'login', username:self.username, password:self.password, protoVersion:1 }));
      };
      ws.onmessage = function(ev){
        let msg; try { msg = JSON.parse(ev.data); } catch(e){ return; }
        if(msg.type === 'login_ok'){
          loggedIn = true; clearTimeout(to);
          self.token = msg.token; self.state = 'connected'; self._reconnectAttempts = 0; self._emit();
          resolve();
        } else if(msg.type === 'login_err'){
          clearTimeout(to); try{ ws.close(); }catch(e){}
          self.state = 'local'; self._emit();
          reject(new Error(msg.reason || 'login failed'));
        } else if(msg.type === 'reply'){
          const p = self._pending[msg.id];
          if(p){
            delete self._pending[msg.id];
            if(msg.ok) p.resolve(msg.data !== undefined ? msg.data : null);
            else p.reject(new Error(msg.error || 'command failed'));
          }
        } else if(msg.type === 'sync'){
          if(self.onSync) self.onSync(msg.cmd, msg.args || {});
        } else if(msg.type === 'snapshot'){
          if(self.onSnapshot) self.onSnapshot(msg.state);
        } else if(msg.type === 'library_changed'){
          if(self.onLibraryChanged) self.onLibraryChanged();
        }
      };
      ws.onclose = function(){ self._onDrop(loggedIn); };
      ws.onerror = function(){ };
    });
  },

  _onDrop(wasLoggedIn){
    const self = this;
    Object.keys(this._pending).forEach(function(id){
      try { self._pending[id].reject(new Error('connection lost')); } catch(e){}
      delete self._pending[id];
    });
    this.ws = null;
    if(this._userClosed){ this.state = 'local'; this.token = null; this._emit(); return; }
    const wasActive = wasLoggedIn || this.state === 'connected' || this.state === 'reconnecting';
    if(wasActive && this._reconnectAttempts < 6){
      this.state = 'reconnecting'; this.token = null; this._emit();
      this._reconnectAttempts++;
      const delay = Math.min(1000 * Math.pow(2, this._reconnectAttempts - 1), 8000);
      this._reconnectTimer = setTimeout(function(){
        self._open().then(function(){ if(self.onReconnected) self.onReconnected(); }).catch(function(){});
      }, delay);
    } else {
      this.state = 'local'; this.token = null; this._emit();
    }
  },

  invoke(cmd, args){
    const self = this;
    if(this.state !== 'connected' || !this.ws){ return Promise.reject(new Error('not connected')); }
    const id = this._id++;
    return new Promise(function(resolve, reject){
      self._pending[id] = { resolve: resolve, reject: reject };
      try { self.ws.send(JSON.stringify({ type:'cmd', id:id, cmd:cmd, args:args || {}, token:self.token })); }
      catch(e){ delete self._pending[id]; reject(e); }
    });
  },

  disconnect(){
    this._userClosed = true;
    if(this._reconnectTimer){ clearTimeout(this._reconnectTimer); this._reconnectTimer = null; }
    if(this.ws){ try { this.ws.close(); } catch(e){} }
    this.ws = null; this.state = 'local'; this.token = null; this._emit();
  },
};

/* Single routable invoke: remote when connected, local Tauri otherwise. */
function BSInvoke(cmd, args){
  if(RemoteLink.isConnected()){ return RemoteLink.invoke(cmd, args); }
  if(window.__TAURI__ && window.__TAURI__.core){
    // Pi-local STATE change while a client watches → broadcast it (fire-and-forget, pre-applied via args).
    if(RemoteLink.hasRemoteClient && typeof STATE_SYNC_CMDS !== 'undefined' && STATE_SYNC_CMDS[cmd]){
      try { window.__TAURI__.core.invoke('sync_broadcast', { cmd: cmd, args: args || {} }); } catch(e){}
    }
    // Pi-local LIBRARY change → after it COMPLETES, tell the client to refresh its list.
    if(RemoteLink.hasRemoteClient && typeof LIBRARY_CMDS !== 'undefined' && LIBRARY_CMDS[cmd]){
      return window.__TAURI__.core.invoke(cmd, args).then(function(r){
        try { window.__TAURI__.core.invoke('notify_library_changed', {}); } catch(e){}
        return r;
      });
    }
    return window.__TAURI__.core.invoke(cmd, args);
  }
  return Promise.resolve();
}
/* Always-local invoke (bypasses RemoteLink) — for client-side ops that must run
 * on THIS machine even while remote: the file picker and local file reads. */
function LocalInvoke(cmd, args){
  if(window.__TAURI__ && window.__TAURI__.core){ return window.__TAURI__.core.invoke(cmd, args); }
  return Promise.reject(new Error('local Tauri unavailable'));
}

/* Expose globally so all vendored-Babel scripts can reach them. */
window.RemoteLink = RemoteLink;
window.BSInvoke = BSInvoke;
window.LocalInvoke = LocalInvoke;

/* Small inline field icons (match the connect mockup) */
function RmIcon({ kind }){
  const common = { width:18, height:18, viewBox:'0 0 24 24', fill:'none', stroke:'#9b5cff', strokeWidth:1.6, strokeLinecap:'round', strokeLinejoin:'round' };
  if(kind==='globe') return (<svg {...common}><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.7 2.5 15.3 0 18M12 3c-2.5 2.7-2.5 15.3 0 18"/></svg>);
  if(kind==='user')  return (<svg {...common}><circle cx="12" cy="8" r="3.4"/><path d="M5 20c0-3.3 3.1-5.5 7-5.5s7 2.2 7 5.5"/></svg>);
  if(kind==='lock')  return (<svg {...common}><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V8a4 4 0 1 1 8 0v3"/></svg>);
  if(kind==='eye')   return (<svg {...common}><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="2.6"/></svg>);
  if(kind==='arrow') return (<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg>);
  return null;
}

/* One labelled input row with leading icon */
function RmField({ icon, label, children }){
  return (
    <div style={{
      display:'flex', alignItems:'center', gap:14,
      background:'rgba(20,18,34,0.65)', border:'1px solid rgba(155,92,255,0.22)',
      borderRadius:14, padding:'12px 16px',
    }}>
      <div style={{flexShrink:0, opacity:0.9}}><RmIcon kind={icon}/></div>
      <div style={{flex:1, minWidth:0}}>
        <div style={{fontFamily:'Rajdhani', fontSize:10, fontWeight:700, letterSpacing:'.22em', color:'#8a7fb5', textTransform:'uppercase'}}>{label}</div>
        {children}
      </div>
    </div>
  );
}

function LoginScreen({ onClose, onConnected }){
  const [ip, setIp] = React.useState('192.168.1.125');
  const [username, setUsername] = React.useState('admin');
  const [password, setPassword] = React.useState('');
  const [showPw, setShowPw] = React.useState(false);
  const [status, setStatus] = React.useState('idle'); // idle | connecting | error
  const [msg, setMsg] = React.useState('');

  const inputStyle = {
    width:'100%', background:'transparent', border:'none', outline:'none',
    color:'#eef0f6', fontFamily:'JetBrains Mono', fontSize:16, fontWeight:600,
    letterSpacing:'.02em', padding:'2px 0 0',
  };

  const onConnect = function(){
    if(!ip.trim()){ setStatus('error'); setMsg('Enter the sampler IP address.'); return; }
    setStatus('connecting'); setMsg('Logging in to ' + ip.trim() + '…');
    RemoteLink.connect(ip.trim(), username.trim() || 'admin', password)
      .then(function(){
        setStatus('idle'); setMsg('');
        if(onConnected) onConnected(ip.trim());
        if(onClose) onClose();
      })
      .catch(function(e){
        setStatus('error');
        setMsg((e && e.message ? e.message : 'connection failed') + ' (' + ip.trim() + ':8420)');
      });
  };

  const onKey = function(e){ if(e.key === 'Enter') onConnect(); };

  return (
    <div style={{
      position:'absolute', inset:0, zIndex:200,
      display:'flex', alignItems:'center', justifyContent:'center',
      background:'radial-gradient(120% 120% at 50% 0%, #12101f 0%, #08070f 60%, #050409 100%)',
    }}>
      {/* ambient waveform glow accents */}
      <div style={{position:'absolute', left:0, top:'42%', opacity:0.18, filter:'blur(1px)'}}><WaveBars count={26} height={120} seed={7} color="#9b5cff"/></div>
      <div style={{position:'absolute', right:0, top:'42%', opacity:0.18, transform:'scaleX(-1)', filter:'blur(1px)'}}><WaveBars count={26} height={120} seed={19} color="#9b5cff"/></div>

      {/* close back to local */}
      <button onClick={onClose} title="Back to local mode"
              style={{position:'absolute', top:16, right:18, background:'none', border:'1px solid rgba(155,92,255,0.3)', color:'#9a8fc0', borderRadius:8, padding:'5px 10px', cursor:'pointer', fontFamily:'JetBrains Mono', fontSize:11, letterSpacing:'.08em'}}>
        ✕ LOCAL
      </button>

      <div style={{width:430, display:'flex', flexDirection:'column', alignItems:'center', gap:18}}>
        {/* brand */}
        <div style={{display:'flex', flexDirection:'column', alignItems:'center', gap:4}}>
          <div style={{transform:'scale(1.6)', marginBottom:8}}><LogoMark size={40}/></div>
          <div style={{fontFamily:'Manrope, Rajdhani', fontWeight:800, fontSize:30, letterSpacing:'.12em', color:'#f2f3f8'}}>BRAYER·SOUND</div>
          <div style={{fontFamily:'Rajdhani', fontSize:11, fontWeight:600, letterSpacing:'.34em', color:'#6f6794', textTransform:'uppercase'}}>sound. engineered to empower.</div>
        </div>

        {/* card */}
        <div style={{
          width:'100%', background:'linear-gradient(180deg, rgba(26,22,42,0.92), rgba(16,14,26,0.92))',
          border:'1px solid rgba(155,92,255,0.28)', borderRadius:20, padding:'22px 24px 24px',
          boxShadow:'0 0 0 1px rgba(155,92,255,0.06), 0 30px 80px rgba(0,0,0,0.6), 0 0 60px rgba(155,92,255,0.12)',
          display:'flex', flexDirection:'column', gap:14,
        }}>
          <div style={{display:'flex', alignItems:'center', justifyContent:'center', gap:10, marginBottom:2}}>
            <div style={{fontFamily:'Rajdhani', fontWeight:700, fontSize:15, letterSpacing:'.28em', color:'#b79bff'}}>CONNECT TO SAMPLER</div>
          </div>
          <div style={{display:'flex', justifyContent:'center', opacity:0.5, marginTop:-6, marginBottom:2}}>
            <WaveBars count={18} height={14} seed={3} color="#9b5cff"/>
          </div>

          <RmField icon="globe" label="IP Address">
            <input style={inputStyle} value={ip} onChange={function(e){ setIp(e.target.value); }} onKeyDown={onKey} placeholder="192.168.1.125" spellCheck={false}/>
          </RmField>
          <RmField icon="user" label="Username">
            <input style={inputStyle} value={username} onChange={function(e){ setUsername(e.target.value); }} onKeyDown={onKey} placeholder="admin" spellCheck={false}/>
          </RmField>
          <RmField icon="lock" label="Password">
            <div style={{display:'flex', alignItems:'center', gap:8}}>
              <input style={inputStyle} type={showPw?'text':'password'} value={password} onChange={function(e){ setPassword(e.target.value); }} onKeyDown={onKey} placeholder="••••••••"/>
              <button onClick={function(){ setShowPw(function(v){ return !v; }); }} title={showPw?'Hide':'Show'} style={{background:'none', border:'none', cursor:'pointer', padding:0, opacity:showPw?1:0.55}}><RmIcon kind="eye"/></button>
            </div>
          </RmField>

          <button onClick={onConnect} disabled={status==='connecting'}
                  style={{
                    marginTop:6, height:52, border:'none', borderRadius:14, cursor:status==='connecting'?'default':'pointer',
                    background:'linear-gradient(180deg, #a96bff, #7b3df0)',
                    boxShadow:'0 10px 30px rgba(123,61,240,0.45), inset 0 1px 0 rgba(255,255,255,0.25)',
                    color:'#fff', fontFamily:'Rajdhani', fontWeight:700, fontSize:16, letterSpacing:'.22em',
                    display:'flex', alignItems:'center', justifyContent:'center', gap:12,
                    opacity:status==='connecting'?0.75:1,
                  }}>
            {status==='connecting' ? (<><span className="save-spin"/>CONNECTING…</>) : (<>CONNECT <RmIcon kind="arrow"/></>)}
          </button>

          {msg && (
            <div style={{
              textAlign:'center', fontFamily:'JetBrains Mono', fontSize:11, lineHeight:1.5, padding:'2px 4px 0',
              color: status==='error' ? '#ff8f8f' : status==='connecting' ? '#9a8fc0' : '#5ee8a8',
            }}>{msg}</div>
          )}
        </div>

        <div style={{fontFamily:'JetBrains Mono', fontSize:10, color:'#4b456a', letterSpacing:'.06em'}}>© 2026 BRAYER·SOUND — remote mode (preview)</div>
      </div>
    </div>
  );
}
