/* Lodestay — the rest of auth: creating an account, verifying it, the four
   screens of a password reset, and the lockout.

   Same shell as sign-in (AGENTS §7.1): no navigation owner, no destinations,
   the mark above the form the only navigation. Every form here is STAGED, and
   every screen shows exactly one state — a validation error and a server error
   are never both on the page.

   Two rules shape almost all of the copy:
     · Nothing confirms or denies that an account exists. The reset flow says
       "if an account exists for that address" and says it on a screen the
       sender reaches whether or not it does.
     · Every number the copy promises comes from AUTH_POLICY in enums.js — the
       link's life, the resend cooldown, the attempts left, the lock — so the
       sentence and the countdown cannot disagree.

   Clocks: a link's expiry is measured against the fixture clock (data.now), the
   same clock every other screen reads. A cooldown or a lockout the reader is
   sitting through is measured against the wall clock, because they are waiting
   in real time. */
(() => {
  const {
    Button, Input, Field, FieldLabel, FieldDescription, FieldError,
    Alert, AlertTitle, AlertDescription, Separator,
  } = window.OriginDesignSystem_7c998b;

  const emailValid = (value) => /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value.trim());
  const mask = (email) => {
    const at_ = email.indexOf("@");
    if (at_ < 2) return email;
    return email.slice(0, 2) + "•".repeat(Math.max(3, at_ - 2)) + email.slice(at_);
  };

  /* One countdown mechanism for the resends and the lock: a wall-clock target,
     re-read every second, printed by the product's one spelling. */
  function useCountdown(target) {
    const [, tick] = React.useState(0);
    React.useEffect(() => {
      if (!target) return undefined;
      const id = window.setInterval(() => tick((n) => n + 1), 500);
      return () => window.clearInterval(id);
    }, [target]);
    return target ? Math.max(0, target - Date.now()) : 0;
  }

  /* Requirements before submission, from the one predicate validation reads. */
  function PasswordChecklist({ password, email }) {
    return (
      <ul className="ls-checklist">
        {AUTH_FLAGS.passwordRules(password, email).map((rule) => (
          <li key={rule.id} className={rule.ok ? "is-ok" : undefined}>
            <Icon name={rule.ok ? "check" : "dot"} size={13} />
            {rule.label}
          </li>
        ))}
      </ul>
    );
  }

  function AuthSwitch({ children }) { return <p className="ls-auth__switch">{children}</p>; }
  function SupportNote() {
    return <span className="ls-note">Stuck? Write to <span className="ls-mono">{AUTH_POLICY.supportEmail}</span> and a person answers.</span>;
  }

  /* ── /signup ───────────────────────────────────────────────────────────
     A sibling of sign-in, differing in three ways only: two extra fields, the
     terms line, and what happens next. */
  function SignUpScreen({ onSubmit, onNavigate }) {
    const [draft, setDraft] = React.useState({ name: "", email: "", password: "" });
    const [reveal, setReveal] = React.useState(false);
    const [submitted, setSubmitted] = React.useState(false);
    const [pending, setPending] = React.useState(false);
    const set = (fields) => setDraft((d) => Object.assign({}, d, fields));

    const errors = {};
    if (draft.name.trim().length < 2) errors.name = "Hosts see this name on your bookings.";
    if (!emailValid(draft.email)) errors.email = "Enter a valid email address.";
    if (!AUTH_FLAGS.passwordOk(draft.password, draft.email)) errors.password = "Your password does not meet the requirements yet.";
    const valid = Object.keys(errors).length === 0;
    const dirty = draft.name.length > 0 || draft.email.length > 0 || draft.password.length > 0;

    return (
      <AuthShell
        quote="Somewhere to sleep, and the same price on the last screen as on the first."
        attribution="Photography drops in here — the frame is the placeholder, not the design."
      >
        <div className="ls-stack">
          <div className="ls-head__text">
            <h1 className="ls-title ls-title--sm">Create account</h1>
            <p className="ls-lede">One account holds your trips, your saved places and your conversations with hosts.</p>
          </div>
          <Field>
            <FieldLabel htmlFor="ls-su-name">Name</FieldLabel>
            <Input id="ls-su-name" autoComplete="name" value={draft.name} invalid={submitted && !!errors.name} onChange={(e) => set({ name: e.target.value })} />
            {submitted && errors.name ? <FieldError>{errors.name}</FieldError> : <FieldDescription>The name a host sees when you book.</FieldDescription>}
          </Field>
          <Field>
            <FieldLabel htmlFor="ls-su-email">Email</FieldLabel>
            <Input id="ls-su-email" type="email" autoComplete="email" value={draft.email} invalid={submitted && !!errors.email} onChange={(e) => set({ email: e.target.value })} />
            {submitted && errors.email ? <FieldError>{errors.email}</FieldError> : null}
          </Field>
          <Field>
            <FieldLabel htmlFor="ls-su-password">Password</FieldLabel>
            <Input
              id="ls-su-password" type={reveal ? "text" : "password"} autoComplete="new-password"
              value={draft.password} invalid={submitted && !!errors.password} onChange={(e) => set({ password: e.target.value })}
              endAddon={<Button variant="ghost" size="icon-xs" aria-label={reveal ? "Hide password" : "Show password"} onClick={() => setReveal(!reveal)}><Icon name={reveal ? "eye-off" : "eye"} size={14} /></Button>}
            />
            <PasswordChecklist password={draft.password} email={draft.email} />
          </Field>
          <Button size="lg" loading={pending} disabled={!dirty} onClick={() => {
            setSubmitted(true);
            if (!valid) return;
            setPending(true);
            window.setTimeout(() => { setPending(false); onSubmit(draft.email.trim()); }, 600);
          }}>Create account</Button>
          <span className="ls-note">
            Creating an account means agreeing to the <button type="button" className="ls-link" onClick={() => onNavigate("/signup")}>terms</button> and the <button type="button" className="ls-link" onClick={() => onNavigate("/signup")}>privacy notice</button>. Nothing is charged until you book.
          </span>
          <AuthSwitch>Already have an account? <button type="button" className="ls-link" onClick={() => onNavigate("/signin")}>Sign in</button></AuthSwitch>
        </div>
      </AuthShell>
    );
  }

  /* ── /verify-email ─────────────────────────────────────────────────────
     What happens next after signup: a screen, not a toast, because the wait is
     real and the address might be wrong. */
  function VerifyEmailScreen({ email, onNavigate }) {
    const [until, setUntil] = React.useState(null);
    const left = useCountdown(until);
    const cooling = left > 0;
    return (
      <AuthShell
        quote="One link, then the whole of Lisbon at the prices the hosts actually set."
        attribution="Nothing is booked and nothing is charged while you finish signing up."
      >
        <div className="ls-stack">
          <div className="ls-head__text">
            <h1 className="ls-title ls-title--sm">Confirm your email</h1>
            <p className="ls-lede">We sent a link to <strong>{email}</strong>. It works once and expires {AUTH_POLICY.verifyLinkHours} hours after it was sent.</p>
          </div>
          <Alert>
            <Icon name="mail" size={16} />
            <AlertTitle>Nothing else to do here</AlertTitle>
            <AlertDescription>Open the link on any device and you land back in Lodestay, signed in.</AlertDescription>
          </Alert>
          <Button variant="outline" size="lg" disabled={cooling} onClick={() => setUntil(Date.now() + AUTH_POLICY.resendCooldownSeconds * 1000)}>
            {cooling ? "Resend link in " + formatClockLeft(left) : "Resend link"}
          </Button>
          <AuthSwitch>Wrong address? <button type="button" className="ls-link" onClick={() => onNavigate("/signup")}>Start again</button> · <button type="button" className="ls-link" onClick={() => onNavigate("/signin")}>Sign in</button></AuthSwitch>
          <SupportNote />
        </div>
      </AuthShell>
    );
  }

  /* ── /forgot-password — request ────────────────────────────────────────
     Enumeration-safe: this screen never says whether the address is known, and
     neither does the one it leads to. */
  function ForgotPasswordScreen({ email, onSubmit, onNavigate }) {
    const [value, setValue] = React.useState(email || "");
    const [submitted, setSubmitted] = React.useState(false);
    const [pending, setPending] = React.useState(false);
    const error = emailValid(value) ? null : "Enter a valid email address.";
    return (
      <AuthShell
        quote="A password is the least interesting thing about a trip. Let's get past it."
        attribution="Your bookings, your dates and your conversations are untouched by this."
      >
        <div className="ls-stack">
          <div className="ls-head__text">
            <h1 className="ls-title ls-title--sm">Reset your password</h1>
            <p className="ls-lede">Tell us the address you book with and we send a link that lasts {AUTH_POLICY.resetLinkMinutes} minutes.</p>
          </div>
          <Field>
            <FieldLabel htmlFor="ls-fp-email">Email</FieldLabel>
            <Input id="ls-fp-email" type="email" autoComplete="email" value={value} invalid={submitted && !!error} onChange={(e) => setValue(e.target.value)} />
            {submitted && error ? <FieldError>{error}</FieldError> : null}
          </Field>
          <Button size="lg" loading={pending} disabled={!value.length} onClick={() => {
            setSubmitted(true);
            if (error) return;
            setPending(true);
            window.setTimeout(() => { setPending(false); onSubmit(value.trim()); }, 600);
          }}>Send reset link</Button>
          <AuthSwitch>Remembered it? <button type="button" className="ls-link" onClick={() => onNavigate("/signin")}>Sign in</button></AuthSwitch>
          <SupportNote />
        </div>
      </AuthShell>
    );
  }

  /* ── /forgot-password/sent — the confirmation ──────────────────────────── */
  function ResetSentScreen({ email, onNavigate }) {
    const [until, setUntil] = React.useState(null);
    const left = useCountdown(until);
    const cooling = left > 0;
    return (
      <AuthShell
        quote="Check the inbox, then come back. The link knows where you left off."
        attribution="Links expire so an old email in a shared inbox cannot open your account."
      >
        <div className="ls-stack">
          <div className="ls-head__text">
            <h1 className="ls-title ls-title--sm">Check your email</h1>
            <p className="ls-lede">If an account exists for {mask(email)}, a reset link is on its way. It expires {AUTH_POLICY.resetLinkMinutes} minutes after it was sent and works once.</p>
          </div>
          <Button variant="outline" size="lg" disabled={cooling} onClick={() => setUntil(Date.now() + AUTH_POLICY.resendCooldownSeconds * 1000)}>
            {cooling ? "Send again in " + formatClockLeft(left) : "Send again"}
          </Button>
          <AuthSwitch><button type="button" className="ls-link" onClick={() => onNavigate("/forgot-password")}>Use a different address</button> · <button type="button" className="ls-link" onClick={() => onNavigate("/signin")}>Back to sign in</button></AuthSwitch>
          <SupportNote />
        </div>
      </AuthShell>
    );
  }

  /* ── /reset-password/:token — set the new one, or the dead link ─────────
     Which of the two renders is derived from the token's expiry against the
     clock. There is no third state: an unknown token is a dead link. */
  function SetNewPasswordScreen({ link, now, onDone, onNavigate }) {
    const [password, setPassword] = React.useState("");
    const [reveal, setReveal] = React.useState(false);
    const [submitted, setSubmitted] = React.useState(false);
    const [pending, setPending] = React.useState(false);
    const dead = AUTH_FLAGS.linkExpired(link, now);

    if (dead) {
      return (
        <AuthShell
          quote="That link has done its hour. The next one takes a minute to ask for."
          attribution="Nothing was changed, and the old password still works."
        >
          <div className="ls-stack">
            <div className="ls-head__text">
              <h1 className="ls-title ls-title--sm">This link has expired</h1>
              <p className="ls-lede">
                {link
                  ? "It was sent " + formatWhen(link.requestedAt, now) + " and lasted " + AUTH_POLICY.resetLinkMinutes + " minutes."
                  : "Reset links last " + AUTH_POLICY.resetLinkMinutes + " minutes and work once."}
                {" "}Ask for a new one and it arrives at the same address.
              </p>
            </div>
            <Button size="lg" onClick={() => onNavigate("/forgot-password")}>Send a new link</Button>
            <AuthSwitch><button type="button" className="ls-link" onClick={() => onNavigate("/signin")}>Back to sign in</button></AuthSwitch>
            <SupportNote />
          </div>
        </AuthShell>
      );
    }

    const ok = AUTH_FLAGS.passwordOk(password, link.email);
    return (
      <AuthShell
        quote="One field, three rules, and then the part of the evening you were actually planning."
        attribution="Setting a new password ends every other session on this account."
      >
        <div className="ls-stack">
          <div className="ls-head__text">
            <h1 className="ls-title ls-title--sm">Set new password</h1>
            <p className="ls-lede">For {mask(link.email)}. This link is good for another {formatMinutesLeft(link.expiresAt - now)}.</p>
          </div>
          <Field>
            <FieldLabel htmlFor="ls-np-password">New password</FieldLabel>
            <Input
              id="ls-np-password" type={reveal ? "text" : "password"} autoComplete="new-password"
              value={password} invalid={submitted && !ok} onChange={(e) => setPassword(e.target.value)}
              endAddon={<Button variant="ghost" size="icon-xs" aria-label={reveal ? "Hide password" : "Show password"} onClick={() => setReveal(!reveal)}><Icon name={reveal ? "eye-off" : "eye"} size={14} /></Button>}
            />
            <PasswordChecklist password={password} email={link.email} />
            {submitted && !ok ? <FieldError>Your password does not meet the requirements yet.</FieldError> : null}
          </Field>
          <Button size="lg" loading={pending} disabled={!password.length} onClick={() => {
            setSubmitted(true);
            if (!ok) return;
            setPending(true);
            window.setTimeout(() => { setPending(false); onDone(); }, 600);
          }}>Set new password</Button>
          <SupportNote />
        </div>
      </AuthShell>
    );
  }

  /* ── /reset-password/done ──────────────────────────────────────────────── */
  function ResetDoneScreen({ onNavigate }) {
    return (
      <AuthShell
        quote="Done. Your trips, your saved places and your conversations are where you left them."
        attribution="Every other device was signed out — that is what a reset is for."
      >
        <div className="ls-stack">
          <div className="ls-head__text">
            <h1 className="ls-title ls-title--sm">Password changed</h1>
            <p className="ls-lede">Every session on this account was ended, including any device you no longer have. Sign in with the new password to carry on.</p>
          </div>
          <Button size="lg" onClick={() => onNavigate("/signin")}>Sign in</Button>
          <SupportNote />
        </div>
      </AuthShell>
    );
  }

  /* ── /account-locked ───────────────────────────────────────────────────
     A designed state with a bounded wait and a way out, not a toast. The wait
     is real: the countdown runs against the wall clock from the moment the
     lock was set. */
  function AccountLockedScreen({ until, onNavigate }) {
    const [target] = React.useState(() => until || Date.now() + AUTH_POLICY.lockMinutes * 60000);
    const left = useCountdown(target);
    const over = left <= 0;
    return (
      <AuthShell
        quote="Someone got the password wrong three times. If it was you, this is a short wait."
        attribution="Nothing about your trips changed while the account was locked."
      >
        <div className="ls-stack">
          <div className="ls-head__text">
            <h1 className="ls-title ls-title--sm">Account locked</h1>
            <p className="ls-lede">{AUTH_POLICY.signInAttempts} wrong passwords in a row locks the account for {AUTH_POLICY.lockMinutes} minutes. Nothing else about it has changed.</p>
          </div>
          {over ? (
            <Alert variant="success">
              <Icon name="lock-open" size={16} />
              <AlertTitle>The wait is over</AlertTitle>
              <AlertDescription>Sign in again, or reset the password if you would rather not guess.</AlertDescription>
            </Alert>
          ) : (
            <Alert variant="warning">
              <Icon name="lock" size={16} />
              <AlertTitle>Unlocks in {formatClockLeft(left)}</AlertTitle>
              <AlertDescription>Resetting the password unlocks it straight away — the link goes to the address on the account.</AlertDescription>
            </Alert>
          )}
          <Button size="lg" disabled={!over} onClick={() => onNavigate("/signin")}>{over ? "Sign in" : "Sign in — " + formatClockLeft(left)}</Button>
          <Button variant="outline" size="lg" onClick={() => onNavigate("/forgot-password")}>Reset your password</Button>
          <SupportNote />
        </div>
      </AuthShell>
    );
  }

  Object.assign(window, {
    SignUpScreen, VerifyEmailScreen, ForgotPasswordScreen, ResetSentScreen,
    SetNewPasswordScreen, ResetDoneScreen, AccountLockedScreen, PasswordChecklist,
  });
})();
