/* Uleez — Login (split screen) */
(function () {
  const { useState, useEffect } = React;
  const Icon = window.Icon;
  const { Field, Button } = window;

  /** Raccourci abonné aux changements de langue, pour les composants de cet écran. */
  const useTL = () => (window.I18N ? I18N.useT() : ((k) => k));
  /** Version hors composant (règles de mot de passe, listes constantes). */
  const tL = (k, p) => (window.I18N ? I18N.t(k, p) : k);

  // Mirrors PASSWORD_POLICY_REGEX server-side (auth.module.ts). Kept in sync deliberately:
  // this only drives the live hints — the server is the one that enforces it.
  // `labelKey` et non `label` : cette liste est une constante de module, évaluée au
  // chargement, alors que la langue peut changer sans rechargement. La résolution se fait au
  // rendu, dans PwdChecklist.
  const PWD_RULES = [
    { labelKey: 'pwd.minLength', test: (v) => v.length >= 8 },
    { labelKey: 'pwd.upper', test: (v) => /[A-Z]/.test(v) },
    { labelKey: 'pwd.digit', test: (v) => /\d/.test(v) },
    { labelKey: 'pwd.special', test: (v) => /[^A-Za-z0-9]/.test(v) },
  ];
  const pwdValid = (v) => PWD_RULES.every((r) => r.test(v));

  /* Split-screen shell shared by login, forgot-password and reset-password. */
  function AuthShell({ children }) {
    const t = useTL();
    return React.createElement('div', { className: 'login' },
      React.createElement('div', { className: 'login-left' },
        React.createElement('div', { className: 'll-grain' }),
        React.createElement('div', { className: 'll-routes' }, routesSvg()),
        React.createElement('div', { className: 'll-top' },
          React.createElement('img', { src: 'icons/Icon-192.png', className: 'brand-mark', style: { width: 44, height: 44, objectFit: 'cover', background: 'transparent' } }),
          React.createElement('div', { className: 'll-brand' }, 'Ul', React.createElement('span', null, 'eez'))),
        React.createElement('div', { className: 'll-mid' },
          React.createElement('h1', null, t('login.heroTitle')),
          React.createElement('p', null, t('login.heroSub'))),
        React.createElement('div', { className: 'll-stats' },
          [['1 200+', t('login.vehicles')], ['58', t('login.wilayas')], ['96 %', t('login.satisfaction')]].map((s, i) =>
            React.createElement('div', { key: i, className: 'll-stat' },
              React.createElement('div', { className: 'v' }, s[0]),
              React.createElement('div', { className: 'l' }, s[1]))))),
      React.createElement('div', { className: 'login-right' }, children),
    );
  }

  function AuthMark() {
    return React.createElement('div', { className: 'lf-mark' },
      React.createElement('img', { src: 'icons/Icon-192.png', className: 'brand-mark', style: { objectFit: 'cover', background: 'transparent' } }));
  }

  function PwdChecklist({ value }) {
    const t = useTL();
    return React.createElement('ul', { className: 'lf-rules' },
      PWD_RULES.map((r, i) => {
        const ok = r.test(value);
        return React.createElement('li', { key: i, className: ok ? 'ok' : '' },
          ok ? React.createElement(Icon, { name: 'checkCircle', size: 14 })
             : React.createElement('span', { className: 'lf-rule-dot' }),
          t(r.labelKey));
      }));
  }

  /**
   * Shown exactly once, right after the encryption key is created.
   *
   * Losing this code is not a nuisance: a password reset cannot re-seal a key it cannot
   * open, so without the code the account's whole encrypted history becomes unreadable.
   * The screen therefore forces an explicit acknowledgement rather than a dismissable toast.
   */
  function RecoveryCodeScreen({ code, onDone }) {
    const t = useTL();
    const [copied, setCopied] = useState(false);
    const [ack, setAck] = useState(false);
    return React.createElement(AuthShell, null,
      React.createElement('div', { className: 'login-form' },
        React.createElement(AuthMark, null),
        React.createElement('h2', null, t('rec.title')),
        React.createElement('p', { className: 'muted', style: { fontSize: 13.5, lineHeight: 1.6 } },
          t('rec.body1'),
          React.createElement('strong', null, t('rec.body2')),
          t('rec.body3')),
        React.createElement('div', {
          style: {
            fontFamily: 'SpaceMono, monospace', fontSize: 19, letterSpacing: 1.5, textAlign: 'center',
            padding: '16px 12px', margin: '16px 0', borderRadius: 12,
            background: 'var(--slate-100)', border: '1px dashed var(--line)', userSelect: 'all',
          },
        }, code),
        React.createElement(Button, {
          variant: 'secondary', icon: copied ? 'checkCircle' : 'clipboard', style: { width: '100%' },
          onClick: () => {
            navigator.clipboard?.writeText(code).then(() => setCopied(true)).catch(() => setCopied(true));
          },
        }, copied ? t('rec.copied') : t('rec.copy')),
        React.createElement('label', {
          style: { display: 'flex', gap: 9, alignItems: 'flex-start', margin: '16px 0', fontSize: 13, cursor: 'pointer' },
        },
          React.createElement('input', {
            type: 'checkbox', checked: ack, onChange: (e) => setAck(e.target.checked),
            style: { marginTop: 2 },
          }),
          React.createElement('span', null, t('rec.confirm'))),
        React.createElement(Button, {
          variant: 'primary', style: { width: '100%' }, disabled: !ack, onClick: onDone,
        }, 'Continuer')));
  }

  function Login({ onLogin }) {
    const t = useTL();
    const [mode, setMode] = useState('login'); // 'login' | 'signup' | 'forgot'
    const [phone, setPhone] = useState('');
    // Dernier indicatif utilisé, mémorisé localement : un loueur établi à l'étranger ne doit
    // pas le rechoisir à chaque connexion. Ce n'est pas un secret, d'où localStorage.
    const [dial, setDial] = useState(() => {
      try { return API.dialCodeFor(localStorage.getItem('uleez-dial')).dial; }
      catch (_) { return API.DEFAULT_DIAL; }
    });
    const [pwd, setPwd] = useState('');
    const [show, setShow] = useState(false);
    const [loading, setLoading] = useState(false);
    const [err, setErr] = useState('');
    // One-time recovery code + the login it gates.
    const [recovery, setRecovery] = useState(null);
    const [pendingLogin, setPendingLogin] = useState(null);

    if (recovery && pendingLogin) {
      return React.createElement(RecoveryCodeScreen, {
        code: recovery,
        onDone: () => onLogin(pendingLogin.role, pendingLogin.me),
      });
    }

    if (mode === 'signup') {
      return React.createElement(window.LessorOnboarding, {
        onBack: () => setMode('login'),
        onLogin: () => setMode('login'),
      });
    }

    if (mode === 'forgot') {
      return React.createElement(ForgotPassword, { onBack: () => setMode('login') });
    }

    const submit = async (e) => {
      e.preventDefault();
      // Longueur vérifiée POUR LE PAYS choisi. L'ancien `digits.length < 9` acceptait 9, 12 ou
      // 40 chiffres et un 0 de tête, qui partait tel quel en « +2130661… ».
      const fullPhone = API.toE164(dial, phone);
      if (!fullPhone) { setErr(`Numéro invalide pour ${API.dialCodeFor(dial).name}.`); return; }
      if (!pwd) { setErr(t('login.needPassword')); return; }

      setErr('');
      setLoading(true);
      try {
        const res = await API.auth.login(fullPhone, pwd);
        API.setSession(res); // store access + refresh tokens

        const me = await API.auth.me();
        // Only admin, lessor and support_agent roles can access this back-office
        if (!['admin', 'lessor', 'support_agent'].includes(me.role)) {
          API.clearSession();
          setErr(t('login.notLessor'));
          setLoading(false);
          return;
        }

        // ── End-to-end encryption ────────────────────────────────────────────
        // Done HERE and nowhere else: this is the only moment the browser legitimately
        // holds the plaintext password, which is what unseals the private key. It is used
        // in memory and never stored.
        let recoveryCode = null;
        if (window.E2E) {
          // Legacy account: move it onto client-side derivation while we still have the
          // raw password. Best-effort — a failure only means it retries next login.
          if (res._e2e && res._e2e.kdfVersion === 0 && window.ULC && ULC.available) {
            try {
              const secret = await ULC.deriveAuthSecret(pwd, fullPhone, ULC.KDF_ITERATIONS);
              await API.auth.upgradeKdf(pwd, secret);
            } catch (_) { /* keep the legacy shape for now */ }
          }
          try {
            const r = await E2E.unlock({ userId: me.id, phone: fullPhone, password: pwd });
            if (r.recoveryCode) recoveryCode = r.recoveryCode;
            // r.reason === 'CLÉ_ILLISIBLE' is not fatal here: the session proceeds and the
            // chat surfaces render locked bubbles, offering the recovery code path there.
          } catch (_) {
            // Encryption unavailable: the app still works, encrypted threads show as locked.
          }
        }
        if (recoveryCode) { setRecovery(recoveryCode); setPendingLogin({ role: me.role, me }); setLoading(false); return; }

        onLogin(me.role, me);
      } catch (ex) {
        setErr(ex.message === 'Unauthorized' || ex.message.includes('401')
          ? t('api.badCredentials')
          : (ex.message || t('login.genericError')));
        setLoading(false);
      }
    };

    return React.createElement(AuthShell, null,
        React.createElement('form', { className: 'login-form', onSubmit: submit },
          React.createElement(AuthMark, null),
          React.createElement('h2', null, 'Connexion'),
          React.createElement('p', { className: 'lf-sub' }, 'Accédez à votre espace de gestion'),

          err ? React.createElement('div', { className: 'lf-banner' },
            React.createElement(Icon, { name: 'alert', size: 16 }), err) : null,

          React.createElement(Field, { label: t('login.phone') },
            React.createElement(PhoneInput, {
              dial, national: phone, disabled: loading,
              onChange: (d, n) => {
                setDial(d); setPhone(n);
                try { localStorage.setItem('uleez-dial', d); } catch (_) { /* mode privé */ }
              },
            })),

          React.createElement(Field, { label: t('login.password') },
            React.createElement('div', { className: 'input-wrap' },
              React.createElement('span', { className: 'lead' }, React.createElement(Icon, { name: 'lock', size: 17 })),
              React.createElement('input', {
                className: 'input has-lead has-trail',
                type: show ? 'text' : 'password',
                placeholder: '••••••••',
                value: pwd,
                onChange: (e) => setPwd(e.target.value),
                disabled: loading,
              }),
              React.createElement('button', { type: 'button', className: 'trail', onClick: () => setShow(!show) },
                React.createElement(Icon, { name: show ? 'eyeOff' : 'eye', size: 18 })))),

          React.createElement('div', { className: 'lf-row' },
            React.createElement('label', { className: 'lf-remember' },
              React.createElement('input', { type: 'checkbox', defaultChecked: true }), t('login.rememberMe')),
            React.createElement('a', {
              href: '#', className: 'lf-forgot',
              onClick: (e) => { e.preventDefault(); setMode('forgot'); },
            }, t('login.forgot'))),

          React.createElement(Button, { variant: 'primary', size: 'lg', type: 'submit', className: 'btn-block', loading },
            loading ? 'Connexion…' : 'Se connecter'),

          React.createElement('div', { className: 'lf-foot' },
            'Pas encore partenaire ? ',
            React.createElement('a', { href: '#', onClick: (e) => { e.preventDefault(); setMode('signup'); } }, 'Devenir loueur')),

          // Standalone public pages — deliberately real links, not in-app routes: they must
          // stay reachable while logged out (the app stores require it).
          React.createElement('div', { className: 'lf-foot', style: { marginTop: 10, fontSize: 12.5 } },
            React.createElement('a', { href: '/conditions' }, "Conditions d'utilisation"),
            React.createElement('span', { style: { margin: '0 8px', opacity: 0.5 } }, '·'),
            React.createElement('a', { href: '/confidentialite' }, 'Confidentialité')),
        ),
    );
  }

  /* ── Demande de lien de réinitialisation ─────────────────────────────────── */
  function ForgotPassword({ onBack }) {
    const t = useTL();
    const [email, setEmail] = useState('');
    const [loading, setLoading] = useState(false);
    const [sent, setSent] = useState(false);
    const [err, setErr] = useState('');

    const submit = async (e) => {
      e.preventDefault();
      const value = email.trim();
      if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) { setErr('Adresse email invalide.'); return; }
      setErr(''); setLoading(true);
      try {
        await API.auth.forgotPassword(value);
        // Deliberately neutral: the server answers identically whether or not the address
        // exists, and the UI must not undo that by saying "compte introuvable".
        setSent(true);
      } catch (ex) {
        setErr(ex.message || t('login.genericError'));
      } finally { setLoading(false); }
    };

    if (sent) {
      return React.createElement(AuthShell, null,
        React.createElement('div', { className: 'login-form' },
          React.createElement(AuthMark, null),
          React.createElement('h2', null, t('fp.checkInbox')),
          React.createElement('p', { className: 'lf-sub' },
            t('fp.sentTo'), React.createElement('strong', null, email.trim()),
            t('fp.sentBody')),
          React.createElement('div', { className: 'lf-note' },
            React.createElement(Icon, { name: 'info', size: 15 }),
            t('fp.checkSpam')),
          React.createElement(Button, { variant: 'primary', size: 'lg', className: 'btn-block', onClick: onBack },
            t('fp.backToLogin'))));
    }

    return React.createElement(AuthShell, null,
      React.createElement('form', { className: 'login-form', onSubmit: submit },
        React.createElement(AuthMark, null),
        React.createElement('h2', null, t('fp.title')),
        React.createElement('p', { className: 'lf-sub' },
          t('fp.subtitle')),

        err ? React.createElement('div', { className: 'lf-banner' },
          React.createElement(Icon, { name: 'alert', size: 16 }), err) : null,

        React.createElement(Field, { label: 'Adresse email' },
          React.createElement('div', { className: 'input-wrap' },
            React.createElement('span', { className: 'lead' }, React.createElement(Icon, { name: 'mail', size: 17 })),
            React.createElement('input', {
              className: 'input has-lead',
              type: 'email',
              autoComplete: 'email',
              placeholder: t('fp.emailPlaceholder'),
              value: email,
              onChange: (e) => setEmail(e.target.value),
              disabled: loading,
            }))),

        React.createElement(Button, { variant: 'primary', size: 'lg', type: 'submit', className: 'btn-block', loading },
          loading ? 'Envoi…' : t('fp.sendLink')),

        React.createElement('div', { className: 'lf-foot' },
          React.createElement('a', { href: '#', onClick: (e) => { e.preventDefault(); onBack(); } }, '← Retour à la connexion'))));
  }

  /* ── Définition du nouveau mot de passe (lien reçu par email) ────────────── */
  function ResetPassword({ token, onDone }) {
    const t = useTL();
    const [checking, setChecking] = useState(true);
    // Statut COMPLET du jeton (valid + matériel de clé E2E), pas juste un booléen : la page a
    // besoin de `canRestoreHistory`/`wrapSalt`/blobs pour re-sceller ou faire tourner la clé.
    const [status, setStatus] = useState({ valid: false });
    const [pwd, setPwd] = useState('');
    const [confirm, setConfirm] = useState('');
    const [recovery, setRecovery] = useState('');
    const [show, setShow] = useState(false);
    const [loading, setLoading] = useState(false);
    const [done, setDone] = useState(false);
    const [newCode, setNewCode] = useState(null); // nouveau code après une rotation
    const [err, setErr] = useState('');
    const valid = !!status.valid;

    // Tell the user the link is dead BEFORE they type a password into it.
    useEffect(() => {
      let alive = true;
      API.auth.checkResetToken(token).then((st) => {
        if (!alive) return;
        setStatus(st && typeof st === 'object' ? st : { valid: false });
        setChecking(false);
      });
      return () => { alive = false; };
    }, [token]);

    const submit = async (e) => {
      e.preventDefault();
      if (!pwdValid(pwd)) { setErr(t('rp.criteria')); return; }
      if (pwd !== confirm) { setErr(t('rp.mismatchDot')); return; }
      setErr(''); setLoading(true);
      try {
        // Clé E2E jointe au reset, comme sur mobile :
        //  - code fourni + restaurable → re-scellement (historique préservé) ;
        //  - sinon, si le compte a un historique chiffré → rotation (identité neuve, ancien
        //    historique perdu, nouveau code montré) pour que la messagerie reparte ;
        //  - compte sans clé → simple reset, l'enrôlement se fera au login.
        let reseal = null;
        let generatedCode = null;
        const code = recovery.trim();
        if (window.E2E) {
          try {
            if (status.canRestoreHistory && code) {
              reseal = await window.E2E.resealForReset({
                recoveryCode: code,
                newPassword: pwd,
                recoveryWrappedPrivateKey: status.recoveryWrappedPrivateKey,
                recoverySalt: status.recoverySalt,
                wrapSalt: status.wrapSalt,
                kdfIterations: status.kdfIterations,
              });
            } else if (status.hasEncryptedHistory) {
              const rot = await window.E2E.buildFreshIdentityForReset(pwd);
              reseal = rot.payload;
              generatedCode = rot.recoveryCode;
            }
          } catch (_) {
            // Code faux : surtout NE PAS réinitialiser (sinon le code resterait le seul moyen
            // d'ouvrir la clé, et l'utilisateur vient d'en saisir un mauvais).
            setErr(t('rp.recoveryWrong')); setLoading(false); return;
          }
        }
        await API.auth.resetPassword(token, pwd, reseal);
        setNewCode(generatedCode);
        setDone(true);
      } catch (ex) {
        setErr(ex.message || t('login.genericError'));
        setLoading(false);
      }
    };

    if (checking) {
      return React.createElement(AuthShell, null,
        React.createElement('div', { className: 'login-form' },
          React.createElement(AuthMark, null),
          React.createElement('p', { className: 'lf-sub' }, 'Vérification du lien…')));
    }

    if (done) {
      // Rotation : on présente le NOUVEAU code de récupération avec le même poids qu'à
      // l'enrôlement (même écran, même case « j'ai noté »), avant de renvoyer à la connexion.
      if (newCode) {
        return React.createElement(RecoveryCodeScreen, { code: newCode, onDone });
      }
      return React.createElement(AuthShell, null,
        React.createElement('div', { className: 'login-form' },
          React.createElement(AuthMark, null),
          React.createElement('h2', null, t('rp.updated')),
          React.createElement('p', { className: 'lf-sub' },
            t('rp.updatedBody') + t('rp.sessionsClosed')),
          React.createElement(Button, { variant: 'primary', size: 'lg', className: 'btn-block', onClick: onDone },
            'Se connecter')));
    }

    if (!valid) {
      return React.createElement(AuthShell, null,
        React.createElement('div', { className: 'login-form' },
          React.createElement(AuthMark, null),
          React.createElement('h2', null, t('rp.expired')),
          React.createElement('p', { className: 'lf-sub' },
            t('rp.expiredBody')
            + t('rp.askNew')),
          React.createElement(Button, { variant: 'primary', size: 'lg', className: 'btn-block', onClick: onDone },
            t('fp.backToLogin'))));
    }

    return React.createElement(AuthShell, null,
      React.createElement('form', { className: 'login-form', onSubmit: submit },
        React.createElement(AuthMark, null),
        React.createElement('h2', null, t('rp.title')),
        React.createElement('p', { className: 'lf-sub' }, t('rp.chooseStrong')),

        err ? React.createElement('div', { className: 'lf-banner' },
          React.createElement(Icon, { name: 'alert', size: 16 }), err) : null,

        React.createElement(Field, { label: t('rp.title') },
          React.createElement('div', { className: 'input-wrap' },
            React.createElement('span', { className: 'lead' }, React.createElement(Icon, { name: 'lock', size: 17 })),
            React.createElement('input', {
              className: 'input has-lead has-trail',
              type: show ? 'text' : 'password',
              autoComplete: 'new-password',
              placeholder: '••••••••',
              value: pwd,
              onChange: (e) => setPwd(e.target.value),
              disabled: loading,
            }),
            React.createElement('button', { type: 'button', className: 'trail', onClick: () => setShow(!show) },
              React.createElement(Icon, { name: show ? 'eyeOff' : 'eye', size: 18 })))),

        React.createElement(PwdChecklist, { value: pwd }),

        React.createElement(Field, {
          label: t('rp.confirm'),
          error: confirm && pwd !== confirm ? t('rp.mismatch') : '',
        },
          React.createElement('div', { className: 'input-wrap' },
            React.createElement('span', { className: 'lead' }, React.createElement(Icon, { name: 'lock', size: 17 })),
            React.createElement('input', {
              className: 'input has-lead',
              type: show ? 'text' : 'password',
              autoComplete: 'new-password',
              placeholder: '••••••••',
              value: confirm,
              onChange: (e) => setConfirm(e.target.value),
              disabled: loading,
            }))),

        // Historique chiffré présent → proposer le code de récupération (facultatif). Sans lui,
        // la messagerie repart avec une nouvelle clé (ancien historique perdu).
        status.hasEncryptedHistory ? React.createElement(Field, {
          label: t('rp.recoveryLabel'),
          hint: t('rp.recoveryHint'),
        },
          React.createElement('div', { className: 'input-wrap' },
            React.createElement('span', { className: 'lead' }, React.createElement(Icon, { name: 'shield', size: 17 })),
            React.createElement('input', {
              className: 'input has-lead',
              type: 'text',
              autoComplete: 'off',
              placeholder: '••••-••••-••••',
              value: recovery,
              onChange: (e) => setRecovery(e.target.value),
              disabled: loading,
            }))) : null,

        React.createElement(Button, {
          variant: 'primary', size: 'lg', type: 'submit', className: 'btn-block', loading,
          disabled: !pwdValid(pwd) || pwd !== confirm,
        }, loading ? 'Enregistrement…' : t('rp.submit'))));
  }

  function routesSvg() {
    return React.createElement('svg', { viewBox: '0 0 400 600', fill: 'none', preserveAspectRatio: 'xMidYMid slice', dangerouslySetInnerHTML: { __html: `
      <g stroke="#0d4a37" stroke-width="1.5" opacity="0.55">
        <path d="M-20 120 C 120 80, 220 200, 420 140" />
        <path d="M-20 300 C 140 260, 260 360, 420 300" />
        <path d="M-20 460 C 120 420, 240 520, 420 470" />
        <path d="M60 -20 C 100 160, 40 360, 120 620" />
        <path d="M300 -20 C 260 180, 360 360, 300 620" />
      </g>
      <g fill="#34d399">
        <circle cx="120" cy="106" r="4"/><circle cx="220" cy="332" r="4"/>
        <circle cx="60" cy="200" r="4"/><circle cx="300" cy="290" r="4"/>
        <circle cx="240" cy="496" r="4"/>
      </g>` } });
  }

  Object.assign(window, { Login, ResetPassword });
})();
