(() => {
  const STORAGE_KEY='melumiyachat_cloud_session_v1';
  const ACCOUNT_KEY='melumiyachat_cloud_account_v1';
  const esc=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  function session(){try{return JSON.parse(localStorage.getItem(STORAGE_KEY)||'null')}catch{return null}}
  function account(){try{return JSON.parse(localStorage.getItem(ACCOUNT_KEY)||'null')}catch{return null}}
  function saveSession(data){
    const s={access_token:data.access_token||data.session?.access_token||'',refresh_token:data.refresh_token||data.session?.refresh_token||'',expires_in:data.expires_in||data.session?.expires_in||0,saved_at:Date.now()};
    if(s.access_token)localStorage.setItem(STORAGE_KEY,JSON.stringify(s));
    const a=data.account||null;if(a)localStorage.setItem(ACCOUNT_KEY,JSON.stringify(a));
    return s;
  }
  function saveAccount(a){if(a)localStorage.setItem(ACCOUNT_KEY,JSON.stringify(a));}
  function clear(){localStorage.removeItem(STORAGE_KEY);localStorage.removeItem(ACCOUNT_KEY)}
  async function parse(r){const t=await r.text();let b;try{b=t?JSON.parse(t):{}}catch{b={detail:t}};if(!r.ok)throw new Error(b.detail||b.error||b.message||`HTTP ${r.status}`);return b}
  async function raw(path,opts={}){return parse(await fetch(path,{headers:{'Content-Type':'application/json',...(opts.headers||{})},...opts}))}
  async function refresh(){const s=session();if(!s?.refresh_token)return false;try{const d=await raw('/api/auth/refresh',{method:'POST',body:JSON.stringify({refresh_token:s.refresh_token})});saveSession(d);return true}catch{clear();return false}}
  async function fetchApi(path,opts={},retry=true){
    const s=session();const headers={'Content-Type':'application/json',...(opts.headers||{})};if(s?.access_token)headers.Authorization=`Bearer ${s.access_token}`;
    const r=await fetch(path,{...opts,headers});if(r.status===401&&retry&&s?.refresh_token&&await refresh())return fetchApi(path,opts,false);return parse(r)
  }
  function removeGate(){document.getElementById('melumiyaAuthGate')?.remove()}
  function gateHtml(){return `<div id="melumiyaAuthGate" class="auth-gate"><div class="auth-card"><div class="auth-brand"><div class="auth-mark"><img src="/static/branding/melumiyachat_app_icon.png" alt="MelumiyaChat"></div><div><strong>MelumiyaChat</strong><span>Your persistent RP worlds</span></div></div><div id="authGateNotice" class="auth-notice">Sign in to sync your personas, RPs and memories across devices.</div><label>Email<input id="authEmail" type="email" autocomplete="email" placeholder="you@example.com"></label><label>Password<input id="authPassword" type="password" autocomplete="current-password" placeholder="At least 8 characters"></label><label id="authNameWrap" class="auth-signup-field hidden">Display name<input id="authDisplayName" autocomplete="nickname" placeholder="How you appear in MelumiyaChat"></label><div class="auth-actions"><button id="authLoginBtn" class="primary">Sign in</button><button id="authSignupBtn" class="secondary">Create account</button></div><button id="authSwitchBtn" class="auth-link" type="button">I need to create an account</button><div id="authError" class="auth-error"></div><div class="auth-foot">Cloud account data is stored in the Melumiya EU Supabase project. Your private RP transcript is scoped to your account.</div></div></div>`}
  function showGate(){
    removeGate();document.body.insertAdjacentHTML('beforeend',gateHtml());let signup=false;const nameWrap=document.getElementById('authNameWrap'),switchBtn=document.getElementById('authSwitchBtn'),loginBtn=document.getElementById('authLoginBtn'),signupBtn=document.getElementById('authSignupBtn'),err=document.getElementById('authError');
    function mode(v){signup=v;nameWrap.classList.toggle('hidden',!signup);loginBtn.classList.toggle('hidden',signup);signupBtn.classList.toggle('hidden',!signup);switchBtn.textContent=signup?'I already have an account':'I need to create an account';err.textContent=''}
    mode(false);switchBtn.onclick=()=>mode(!signup);
    return new Promise((resolve)=>{
      loginBtn.onclick=async()=>{err.textContent='';loginBtn.disabled=true;try{const d=await raw('/api/auth/login',{method:'POST',body:JSON.stringify({email:document.getElementById('authEmail').value.trim(),password:document.getElementById('authPassword').value})});saveSession(d);saveAccount(d.account);removeGate();resolve(d.account)}catch(e){err.textContent=e.message}finally{loginBtn.disabled=false}};
      signupBtn.onclick=async()=>{err.textContent='';signupBtn.disabled=true;try{const d=await raw('/api/auth/signup',{method:'POST',body:JSON.stringify({email:document.getElementById('authEmail').value.trim(),password:document.getElementById('authPassword').value,display_name:document.getElementById('authDisplayName').value.trim()})});if(d.needs_email_confirmation){err.className='auth-error auth-good';err.textContent='Account created. Check your email to confirm it, then sign in.';mode(false);return}if(d.session?.access_token){saveSession({session:d.session,account:d.account});saveAccount(d.account);removeGate();resolve(d.account)}else{err.textContent='Account created. Sign in to continue.';mode(false)}}catch(e){err.textContent=e.message}finally{signupBtn.disabled=false}};
    })
  }
  async function ensure(){
    const s=session();
    if(s?.access_token){
      try{const a=await fetchApi('/api/auth/me');saveAccount(a);return a}
      catch{clear()}
    }
    return await showGate()
  }
  async function logout(){const s=session();try{if(s?.access_token)await fetchApi('/api/auth/logout',{method:'POST',body:'{}'},false)}catch{}clear();location.reload()}
  window.MelumiyaAuth={session,account,saveSession,saveAccount,clear,refresh,fetch:fetchApi,ensure,logout};
})();
