// pantry-auth.jsx — Login / Signup overlay + Auth API client
//
// Reads/writes:  localStorage('pantrywise.session')  = { token, user }
// Exposes:
//   window.PWAuth.api       — { signup, login, logout, me, invite, members, syncPull, syncPush }
//   window.PWAuth.useAuth() — { user, status, signIn, signUp, signOut, refresh }
//   window.PWAuth.LoginGate — full-screen Login/Signup overlay (renders children when authed or skipped)

const { useState: useStateAuth, useEffect: useEffectAuth, useCallback: useCallbackAuth, useRef: useRefAuth } = React;

const AUTH_LS_KEY = 'pantrywise.session';
const AUTH_SKIP_LS_KEY = 'pantrywise.authSkipped';

// ── api client ─────────────────────────────────────────────────
function loadSession() {
  try { return JSON.parse(localStorage.getItem(AUTH_LS_KEY) || 'null'); } catch (e) { return null; }
}
function saveSession(s) {
  try {
    if (s) localStorage.setItem(AUTH_LS_KEY, JSON.stringify(s));
    else   localStorage.removeItem(AUTH_LS_KEY);
  } catch (e) {}
}

async function apiFetch(path, opts = {}) {
  const session = loadSession();
  const headers = { 'content-type': 'application/json', ...(opts.headers || {}) };
  if (session?.token) headers['authorization'] = `Bearer ${session.token}`;
  const resp = await fetch('/api' + path, {
    method: opts.method || 'GET',
    headers,
    body: opts.body ? JSON.stringify(opts.body) : undefined,
  });
  let data = null;
  try { data = await resp.json(); } catch (e) {}
  if (!resp.ok) {
    const e = new Error((data && data.error) || `HTTP ${resp.status}`);
    e.status = resp.status;
    throw e;
  }
  return data;
}

const api = {
  signup:    (email, password, name, inviteToken) => apiFetch('/auth/signup', { method: 'POST', body: { email, password, name, inviteToken } }),
  login:     (email, password)                    => apiFetch('/auth/login',  { method: 'POST', body: { email, password } }),
  logout:    ()                                   => apiFetch('/auth/logout', { method: 'POST' }),
  deleteAccount: ()                               => apiFetch('/auth/account', { method: 'DELETE' }),
  forgot:    (email)                              => apiFetch('/auth/forgot', { method: 'POST', body: { email } }),
  resetPassword: (token, password)               => apiFetch('/auth/reset', { method: 'POST', body: { token, password } }),
  pushSubscribe: (subscription)                  => apiFetch('/push/subscribe', { method: 'POST', body: { subscription } }),
  adminStats: ()                                 => apiFetch('/admin/stats'),
  adminUsers: ()                                 => apiFetch('/admin/users'),
  adminDeleteUser: (id)                          => apiFetch('/admin/delete-user', { method: 'POST', body: { id } }),
  me:        ()                                   => apiFetch('/auth/me'),
  invite:    (email)                              => apiFetch('/household/invite',  { method: 'POST', body: { email } }),
  members:   ()                                   => apiFetch('/household/members'),
  syncPull:  ()                                   => apiFetch('/sync'),
  syncPush:  (data)                               => apiFetch('/sync', { method: 'POST', body: { data } }),
  aiScan:    (image, mediaType, mode)             => apiFetch('/ai-scan', { method: 'POST', body: { image, mediaType, mode } }),
};

// ── hook ───────────────────────────────────────────────────────
function useAuth() {
  const [session, setSession] = useStateAuth(() => loadSession());
  // status: 'unknown' | 'authed' | 'guest' (chose to skip) | 'unauthed'
  const [status, setStatus] = useStateAuth(() => {
    if (loadSession()) return 'authed';
    try { return localStorage.getItem(AUTH_SKIP_LS_KEY) === '1' ? 'guest' : 'unauthed'; }
    catch (e) { return 'unauthed'; }
  });

  // Refresh session on mount — server may have invalidated it (expired, etc).
  useEffectAuth(() => {
    if (!session?.token) return;
    api.me().then(({ user }) => {
      const next = { token: session.token, user };
      saveSession(next);
      setSession(next);
      setStatus('authed');
    }).catch((e) => {
      // Network error → keep session for offline use. 401 → clear it.
      if (e.status === 401) {
        saveSession(null);
        setSession(null);
        setStatus('unauthed');
      }
    });
  }, []);

  const signIn = useCallbackAuth(async (email, password) => {
    const { user, sessionToken } = await api.login(email, password);
    const next = { token: sessionToken, user };
    saveSession(next);
    setSession(next);
    setStatus('authed');
    try { localStorage.removeItem(AUTH_SKIP_LS_KEY); } catch (e) {}
    return user;
  }, []);

  const signUp = useCallbackAuth(async (email, password, name, inviteToken) => {
    const { user, sessionToken } = await api.signup(email, password, name, inviteToken);
    const next = { token: sessionToken, user };
    saveSession(next);
    setSession(next);
    setStatus('authed');
    try { localStorage.removeItem(AUTH_SKIP_LS_KEY); } catch (e) {}
    return user;
  }, []);

  const signOut = useCallbackAuth(async () => {
    try { await api.logout(); } catch (e) {}
    saveSession(null);
    setSession(null);
    setStatus('unauthed');
  }, []);

  const skipAuth = useCallbackAuth(() => {
    try { localStorage.setItem(AUTH_SKIP_LS_KEY, '1'); } catch (e) {}
    setStatus('guest');
  }, []);

  return { user: session?.user || null, token: session?.token || null, status, signIn, signUp, signOut, skipAuth };
}

// ── extract invite token from URL ──────────────────────────────
function getInviteFromUrl() {
  try {
    const u = new URL(window.location.href);
    return u.searchParams.get('invite') || null;
  } catch (e) { return null; }
}

// ── LoginGate: renders auth screen until user signs in or skips ────
// Uses UNCONTROLLED inputs (refs) instead of controlled React state to
// dodge every browser quirk that can prevent typing in a position:fixed
// PWA standalone shell. We read values straight from the DOM at submit.
function getResetFromUrl() {
  try { return new URL(window.location.href).searchParams.get('reset') || null; } catch (e) { return null; }
}

function LoginGate({ auth, palette, lang, onToggleLang, children }) {
  const [resetToken] = useStateAuth(() => getResetFromUrl());
  const [mode, setMode] = useStateAuth(() => getResetFromUrl() ? 'reset' : 'login'); // login | signup | forgot | reset
  const [busy, setBusy] = useStateAuth(false);
  const [error, setError] = useStateAuth('');
  const [info, setInfo] = useStateAuth('');
  const [inviteToken] = useStateAuth(() => getInviteFromUrl());

  const emailRef = useRefAuth(null);
  const passwordRef = useRefAuth(null);
  const nameRef = useRefAuth(null);

  // If there's an invite token in the URL, force the signup mode
  useEffectAuth(() => {
    if (inviteToken) setMode('signup');
  }, [inviteToken]);

  // Already authed or chose to skip → render the app
  if (auth.status === 'authed' || auth.status === 'guest') {
    return children;
  }

  const submit = async (e) => {
    if (e && e.preventDefault) e.preventDefault();
    const email = (emailRef.current?.value || '').trim();
    const password = passwordRef.current?.value || '';
    const name = (nameRef.current?.value || '').trim();
    setError(''); setInfo(''); setBusy(true);
    try {
      if (mode === 'forgot') {
        await api.forgot(email);
        setInfo(lang === 'fr' ? 'Si un compte existe, un courriel de réinitialisation a été envoyé.' : 'If an account exists, a reset email was sent.');
      } else if (mode === 'reset') {
        await api.resetPassword(resetToken, password);
        setInfo(lang === 'fr' ? 'Mot de passe changé. Connecte-toi.' : 'Password changed. Sign in.');
        setMode('login');
        try { history.replaceState(null, '', '/'); } catch (e) {}
      } else if (mode === 'login') {
        await auth.signIn(email, password);
      } else {
        await auth.signUp(email, password, name, inviteToken);
      }
    } catch (err) {
      setError(err.message || 'Erreur');
    } finally {
      setBusy(false);
    }
  };

  const tt = (fr, en) => lang === 'fr' ? fr : en;

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 300,
      background: palette.bg, color: palette.ink,
      display: 'flex', flexDirection: 'column',
      fontFamily: 'Geist, ui-sans-serif, system-ui, sans-serif',
      paddingTop: 'env(safe-area-inset-top)',
      paddingBottom: 'env(safe-area-inset-bottom)',
      paddingLeft: 'env(safe-area-inset-left)',
      paddingRight: 'env(safe-area-inset-right)',
      boxSizing: 'border-box',
    }}>
      {/* lang toggle */}
      <div style={{ display: 'flex', justifyContent: 'flex-end', padding: '14px 18px 0' }}>
        <div onClick={onToggleLang} style={{
          padding: '6px 10px', borderRadius: 8, background: palette.cardSub,
          color: palette.ink2, border: `1px solid ${palette.line}`,
          fontSize: 11, fontFamily: 'Geist Mono, monospace', fontWeight: 500,
          letterSpacing: 0.06, cursor: 'pointer',
        }}>{lang.toUpperCase()}</div>
      </div>

      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: '0 24px 24px', maxWidth: 440, width: '100%', margin: '0 auto', boxSizing: 'border-box', overflowY: 'auto' }}>
        {/* logo */}
        <div style={{
          width: 64, height: 64, borderRadius: 18, background: palette.primary,
          display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 22,
          fontSize: 32,
        }}>🥬</div>
        <div style={{ fontSize: 28, fontWeight: 600, letterSpacing: -0.7, color: palette.ink, lineHeight: 1.15 }}>
          {mode === 'forgot' ? tt('Mot de passe oublié', 'Forgot password')
            : mode === 'reset' ? tt('Nouveau mot de passe', 'New password')
            : inviteToken
            ? tt('Tu es invité·e à rejoindre une famille', "You've been invited to a household")
            : (mode === 'login' ? tt('Bon retour.', 'Welcome back.') : tt('Crée ton compte.', 'Create your account.'))}
        </div>
        <div style={{ fontSize: 14.5, color: palette.ink2, marginTop: 8, lineHeight: 1.5 }}>
          {mode === 'forgot' ? tt('Entre ton courriel — on t\'envoie un lien pour le réinitialiser.', 'Enter your email — we\'ll send a reset link.')
            : mode === 'reset' ? tt('Choisis un nouveau mot de passe (8 caractères min).', 'Choose a new password (8 chars min).')
            : inviteToken
            ? tt('Crée ton compte pour rejoindre — inventaire, repas et liste seront partagés.', 'Sign up to join — inventory, meals and list will be shared.')
            : tt('Sync tes données sur tous tes appareils et partage avec ta famille.', 'Sync across devices and share with your household.')}
        </div>

        <form onSubmit={submit} style={{ marginTop: 26, display: 'flex', flexDirection: 'column', gap: 12 }}>
          {mode === 'signup' && (
            <AuthField palette={palette} label={tt('Nom', 'Name')}
              inputRef={nameRef} autoComplete="name" placeholder="Alex" autoFocus />
          )}
          {mode !== 'reset' && (
            <AuthField palette={palette} label={tt('Email', 'Email')}
              type="email" inputRef={emailRef}
              autoComplete="email" placeholder="toi@email.com"
              autoFocus={mode === 'login' || mode === 'forgot'} />
          )}
          {mode !== 'forgot' && (
            <AuthField palette={palette} label={tt('Mot de passe', 'Password')}
              type="password" inputRef={passwordRef}
              autoComplete={mode === 'login' ? 'current-password' : 'new-password'}
              placeholder="••••••••" autoFocus={mode === 'reset'} />
          )}

          {mode === 'login' && (
            <div onClick={() => { setMode('forgot'); setError(''); setInfo(''); }} style={{
              fontSize: 13, color: palette.primary, fontWeight: 500, cursor: 'pointer', alignSelf: 'flex-start', marginTop: -4,
            }}>{tt('Mot de passe oublié ?', 'Forgot password?')}</div>
          )}

          {error && (
            <div style={{
              background: palette.redSoft, color: palette.redSoftInk,
              padding: '10px 12px', borderRadius: 10, fontSize: 13, lineHeight: 1.4,
            }}>{error}</div>
          )}
          {info && (
            <div style={{
              background: palette.primarySoft, color: palette.primarySoftInk,
              padding: '10px 12px', borderRadius: 10, fontSize: 13, lineHeight: 1.4,
            }}>{info}</div>
          )}

          <button type="submit" disabled={busy} style={{
            marginTop: 4, height: 50, borderRadius: 14, border: 'none',
            background: palette.primary, color: palette.primaryInk,
            fontSize: 16, fontWeight: 600, cursor: 'pointer',
            opacity: busy ? 0.6 : 1, fontFamily: 'inherit',
            WebkitAppearance: 'none', appearance: 'none',
          }}>
            {busy ? tt('Une seconde…', 'One sec…')
                  : mode === 'forgot' ? tt('Envoyer le lien', 'Send link')
                  : mode === 'reset' ? tt('Changer le mot de passe', 'Change password')
                  : mode === 'login' ? tt('Se connecter', 'Sign in') : tt('Créer mon compte', 'Sign up')}
          </button>
          {(mode === 'forgot' || mode === 'reset') && (
            <div onClick={() => { setMode('login'); setError(''); setInfo(''); }} style={{
              textAlign: 'center', fontSize: 13, color: palette.ink2, cursor: 'pointer', marginTop: 4,
            }}>{tt('← Retour à la connexion', '← Back to sign in')}</div>
          )}
        </form>

        {(mode === 'login' || mode === 'signup') && (
        <div style={{
          marginTop: 18, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
          fontSize: 13.5, color: palette.ink2,
        }}>
          <span>{mode === 'login' ? tt('Pas de compte ?', 'No account?') : tt('Déjà inscrit ?', 'Already have an account?')}</span>
          <span onClick={() => {
            setMode(mode === 'login' ? 'signup' : 'login');
            setError('');
            // refs persist; just clear values so we don't carry over the wrong field
            if (emailRef.current) emailRef.current.value = '';
            if (passwordRef.current) passwordRef.current.value = '';
            if (nameRef.current) nameRef.current.value = '';
          }}
                style={{ color: palette.primary, fontWeight: 600, cursor: 'pointer' }}>
            {mode === 'login' ? tt('Crée-le', 'Sign up') : tt('Connecte-toi', 'Sign in')}
          </span>
        </div>
        )}

        {!inviteToken && (mode === 'login' || mode === 'signup') && (
          <div onClick={auth.skipAuth} style={{
            marginTop: 20, textAlign: 'center', fontSize: 13, color: palette.ink3,
            cursor: 'pointer', padding: '8px',
          }}>
            {tt('Continuer sans compte', 'Continue without an account')} ·{' '}
            <span style={{ color: palette.ink2, fontFamily: 'Geist Mono, monospace' }}>
              {tt('local seulement', 'local only')}
            </span>
          </div>
        )}
      </div>
    </div>
  );
}

function AuthField({ palette, label, type = 'text', inputRef, defaultValue, placeholder, autoComplete, autoFocus }) {
  return (
    <label style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
      <span style={{ fontSize: 11.5, fontFamily: 'Geist Mono, monospace', color: palette.ink2, letterSpacing: 0.04, textTransform: 'uppercase' }}>{label}</span>
      <input
        ref={inputRef}
        type={type}
        defaultValue={defaultValue}
        placeholder={placeholder}
        autoComplete={autoComplete}
        autoFocus={autoFocus}
        autoCapitalize={type === 'email' || type === 'password' ? 'none' : 'sentences'}
        autoCorrect={type === 'email' || type === 'password' ? 'off' : 'on'}
        spellCheck={type === 'email' || type === 'password' ? false : true}
        style={{
          height: 48, padding: '0 14px', borderRadius: 12,
          border: `1px solid ${palette.line}`, background: palette.card,
          fontSize: 16, /* 16+ avoids iOS Safari auto-zoom on focus */
          color: palette.ink, fontFamily: 'inherit', outline: 'none',
          boxSizing: 'border-box', width: '100%',
          WebkitAppearance: 'none', appearance: 'none',
          pointerEvents: 'auto',
        }}
      />
    </label>
  );
}

// expose
window.PWAuth = { api, useAuth, LoginGate, loadSession, saveSession, AUTH_LS_KEY };
Object.assign(window, { PWLoginGate: LoginGate, usePWAuth: useAuth, pwApi: api });
