// /authorize/ — the disclosure step a visitor completes before a free scan runs.
//
// Sekura executes real attacks. The Contract Stack Guide puts it plainly:
// "Without written authorization identifying the assets in scope, an assessment
// is potentially unauthorized." For Business and Enterprise that instrument is a
// countersigned Authorization to Test; for self-serve it is the authorization
// warranty in the Online Terms, recorded against one named target.
//
// Two routes to that, because they carry different weight:
//
//   verified   sign in with GitHub, pick a repository you administer. Control is
//              proven rather than claimed. This matters under California Penal
//              Code § 502 — cloning a repo is *copying data*, and § 502(e) lets
//              the owner sue over it, so an assertion by a stranger is not
//              permission from the party who could actually grant it.
//   asserted   paste a URL and warrant that you may authorize it. Backed by the
//              indemnity and right to suspend in Terms §4.4–4.5.
//
// Both record the same signature: a typed name (the operative electronic
// signature — intent plus attribution) and a drawn mark with the metrics that
// show a person made it, which is what an audit pack wants to see.
//
// Copy tracks Contract Templates/contracts/02-Online-Terms-of-Service.docx §4
// and 04-Authorization-to-Test.docx §3 via data/scanAuthorization.js.

// "https://github.com/Owner/Repo.git/" and "owner/repo" are the same target.
// Compared on the normalised owner/name pair rather than by substring, so
// "acme/api" cannot match "acme/api-internal".
function sameRepo(a, b) {
  const norm = (v) => String(v || '')
    .trim().toLowerCase()
    .replace(/^https?:\/\//, '')
    .replace(/^(www\.)?(github|gitlab|bitbucket)\.(com|org)\//, '')
    .replace(/\.git$/, '')
    .replace(/\/+$/, '');
  const x = norm(a); const y = norm(b);
  return !!x && !!y && x === y;
}

function AuthorizeView() {
  const { React } = window;
  const { useState, useEffect, useMemo, useRef } = React;

  const terms = window.ScanAuthTerms || [];
  const clauseRefs = window.ScanAuthClauseRefs || '';
  const roe = window.ScanAuthRulesOfEngagement || [];

  const params = typeof window !== 'undefined'
    ? new URLSearchParams(window.location.search) : new URLSearchParams();

  const [repo, setRepo] = useState((params.get('repo') || '').trim());
  const [isPrivate, setIsPrivate] = useState(false);
  const [repoToken, setRepoToken] = useState('');
  const [name, setName] = useState('');
  const [title, setTitle] = useState('');
  const [org, setOrg] = useState('');
  const [email, setEmail] = useState('');
  const [signature, setSignature] = useState('');
  const [drawn, setDrawn] = useState(null);
  const [agreed, setAgreed] = useState(false);
  const [featureConsent, setFeatureConsent] = useState(false);
  const [attempted, setAttempted] = useState(false);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState(null);
  const [done, setDone] = useState(null);

  // GitHub verification state, restored from the redirect.
  const [grant] = useState(params.get('granted') || '');
  const [verifyErr, setVerifyErr] = useState(params.get('verr') || '');
  const [account, setAccount] = useState(null);
  const [repos, setRepos] = useState(null);
  const [picked, setPicked] = useState(null);
  const [loadingRepos, setLoadingRepos] = useState(false);
  const [ent, setEnt] = useState(null);   // what is free vs paid for this account
  const hpRef = useRef(null);

  useEffect(() => { document.title = 'Sekura — authorize a scan'; }, []);

  // Returning from Stripe. The credit is granted by the webhook, which can land
  // a moment after the redirect, so this is a reassurance message rather than a
  // gate — the submit will succeed once the credit is on the account.
  const justPaid = params.get('paid') === '1';

  // Clear the grant out of the address bar once read: it is short-lived, but a
  // token has no business sitting in browser history or a shared screenshot.
  useEffect(() => {
    if (!grant && !verifyErr) return;
    const u = new URL(window.location.href);
    ['granted', 'verr'].forEach((k) => u.searchParams.delete(k));
    window.history.replaceState({}, '', u.toString());
  }, []);

  useEffect(() => {
    if (!grant) return;
    setLoadingRepos(true);
    fetch(`/api/github/repos?grant=${encodeURIComponent(grant)}`)
      .then((r) => r.json())
      .then((d) => {
        if (!d.ok) { setVerifyErr(d.error || 'verification failed'); return; }
        setAccount({ login: d.login, name: d.name, email: d.email });
        setRepos(d.repos || []);
        if (d.name) setName((v) => v || d.name);
        if (d.email) setEmail((v) => v || d.email);
        // Fill the form in from what GitHub just told us. Coming back from an
        // install with an empty box is the wrong end of the trade — the user
        // has just picked the repository on GitHub's own screen, and being
        // asked to type it again reads as though the grant did not register.
        const list = d.repos || [];
        const match = list.find((r) => sameRepo(repo, r.fullName));
        const chosen = match || (list.length === 1 ? list[0] : null);
        if (chosen) { setPicked(chosen); setRepo(`github.com/${chosen.fullName}`); }
      })
      .catch(() => setVerifyErr('could not reach GitHub'))
      .finally(() => setLoadingRepos(false));

    // What this account may scan, so the list can be labelled before anyone
    // fills anything in. Discovering the paywall only after signing is the
    // wrong order — all the work is already done by then.
    fetch(`/api/scan-entitlement?grant=${encodeURIComponent(grant)}`)
      .then((r) => r.json())
      .then((d) => { if (d.ok) setEnt(d); })
      .catch(() => { /* labels are a nicety; submit still enforces */ });
  }, [grant]);

  // Does choosing this repository cost anything? The first distinct repo is
  // free, an already-authorized one never costs again, and credits or a
  // subscription cover the rest.
  const repoCost = (fullName) => {
    if (!ent || !ent.identified) return 'free';
    if ((ent.authorized || []).some((u) => sameRepo(u, fullName))) return 'authorized';
    if (!ent.freeScanUsed) return 'free';
    if (ent.subscription) return 'subscription';
    if (ent.credits > 0) return 'credit';
    return 'upgrade';
  };
  const pickedCost = picked ? repoCost(picked.fullName) : null;
  const needsUpgrade = pickedCost === 'upgrade';

  const verified = !!(account && picked);
  const repoValid = useMemo(
    () => !!(window.githubLib && window.githubLib.validate(repo).ok),
    [repo],
  );

  const missing = [];
  if (!repoValid) missing.push('a valid repository URL');
  if (needsUpgrade) missing.push('an upgrade for this repository');
  if (!verified && isPrivate && !repoToken.trim()) missing.push('a clone token for the private repository');
  if (!agreed) missing.push('the authorization terms');
  if (!name.trim()) missing.push('your name');
  if (!/.+@.+\..+/.test(email)) missing.push('a valid email');
  if (signature.trim().toLowerCase() !== name.trim().toLowerCase() || !signature.trim()) {
    missing.push('a typed signature matching your name');
  }
  if (!drawn || !drawn.image) missing.push('your signature in the box');
  const firstMissing = missing[0] || null;

  const submit = async (e) => {
    e.preventDefault();
    setAttempted(true);
    setError(null);
    if (firstMissing) return;
    setSubmitting(true);
    try {
      const res = await fetch('/api/free-scan', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          repoUrl: repo,
          email: email.trim(),
          consentScan: true,
          consentFeature: featureConsent,
          isPrivate: verified ? !!(picked && picked.private) : isPrivate,
          repoToken: !verified && isPrivate ? repoToken.trim() : '',
          hp: hpRef.current ? hpRef.current.value : '',
          turnstileToken: window.__sekuraTurnstileToken || undefined,
          authorization: {
            version: window.ScanAuthVersion,
            agreed: true,
            signerName: name.trim(),
            signerTitle: title.trim(),
            signerOrg: org.trim(),
            signatureText: signature.trim(),
            signatureImage: drawn.image,
            signatureMeta: drawn.meta,
            grant: verified ? grant : undefined,
            repoId: verified ? picked.id : undefined,
          },
        }),
      });
      const data = await res.json().catch(() => ({}));
      if (!data.ok) {
        // Out of free scans: hand off to the payment page, carrying the repo so
        // they come back to the same target rather than starting over.
        if (res.status === 402 && data.upgradeUrl) {
          try { window.sessionStorage.setItem('sekura.grant', grant || ''); } catch { /* private mode */ }
          window.location.href = data.upgradeUrl;
          return;
        }
        setError(data.error || 'Something went wrong. Try again, or email hello@sekura.ai.');
        return;
      }
      setDone(data);
      window.scrollTo({ top: 0, behavior: 'smooth' });
    } catch {
      setError('Network error. Try again, or email hello@sekura.ai.');
    } finally {
      setSubmitting(false);
    }
  };

  if (done) {
    return (
      <div className="ah-auth">
        <section className="ah-auth-hero">
          <div className="ah-auth-inner ah-auth-done">
            <p className="ah-eyebrow">Authorized</p>
            <h1 className="ah-h1">Your scan is queued.</h1>
            <p className="ah-sub">
              We have your authorization for <strong>{repo}</strong> and the full report
              is on its way to <strong>{email}</strong>.
              {done.reference && <> Your authorization reference is <strong>{done.reference}</strong>.</>}
            </p>
            <p className="ah-body ah-auth-done-body">
              You can withdraw this authorization at any time by replying to that email
              or writing to <a className="ah-link" href="mailto:hello@sekura.ai">hello@sekura.ai</a>.
              We will stop immediately.
            </p>
            <div className="ah-auth-done-ctas">
              <a className="ah-btn" href="/">Back to the homepage</a>
              <a className="ah-link" href="/pricing/">See what a paid envelope covers<span className="ah-chev">›</span></a>
            </div>
          </div>
        </section>
      </div>
    );
  }

  const VERIFY_ERRORS = {
    denied: 'You cancelled the GitHub sign-in. You can still authorize by pasting a URL below.',
    expired: 'That verification link expired. Start again if you want to verify with GitHub.',
    unconfigured: 'GitHub verification is not switched on yet — authorize by URL below.',
    exchange_failed: 'GitHub did not complete the sign-in. Try again, or authorize by URL.',
    profile_failed: 'We could not read your GitHub account. Try again, or authorize by URL.',
    bad_request: 'That sign-in did not come back correctly. Try again.',
  };

  return (
    <div className="ah-auth">
      <section className="ah-auth-hero">
        <div className="ah-auth-inner">
          <p className="ah-eyebrow">Before we scan</p>
          <h1 className="ah-h1">Authorize the scan.</h1>
          <p className="ah-sub">
            Sekura runs real attacks against the target you give it. That is the point —
            and it is why we need you to confirm, on the record, that you are entitled to
            authorize testing against this repository.
          </p>
        </div>
      </section>

      <form className="ah-auth-body" onSubmit={submit} noValidate>
        <div className="ah-auth-inner ah-auth-grid">
          <div className="ah-auth-main">

            <section className="ah-auth-block">
              <h2 className="ah-h3">The target</h2>
              <p className="ah-body ah-auth-note">
                This is the only asset you are authorizing. We will not scan anything else,
                and we will not follow it anywhere it deploys to.
              </p>

              {justPaid && (
                <p className="ah-auth-paid">
                  <window.AhCheck /> Payment received — finish signing below and the
                  scan will start.
                </p>
              )}

              {verifyErr && (
                <p className="ah-auth-err">
                  {VERIFY_ERRORS[verifyErr] || 'Verification failed. You can still authorize by URL.'}
                </p>
              )}

              {!account && (
                <div className="ah-auth-verify">
                  <div>
                    <p className="ah-auth-verify-t">Verify with GitHub</p>
                    <p className="ah-auth-verify-b ah-small">
                      Choose the repositories you want to grant. Sekura asks for read
                      access to their contents and nothing else — it cannot write to them.
                      What you grant is the authorization, and it saves entering a clone
                      token for a private repo.
                    </p>
                  </div>
                  <div className="ah-auth-verify-actions">
                    <a
                      className="ah-btn"
                      href={`/api/auth/github/start?mode=app&repo=${encodeURIComponent(repo)}`}
                    >
                      Install the Sekura app
                    </a>
                    {/* Some organizations block app installation without an
                        admin, so the broader OAuth route stays available. */}
                    <a
                      className="ah-auth-alt"
                      href={`/api/auth/github/start?mode=oauth&repo=${encodeURIComponent(repo)}`}
                    >
                      or authorize with OAuth
                    </a>
                  </div>
                </div>
              )}

              {account && (
                <div className="ah-auth-verified">
                  <p className="ah-auth-verified-t">
                    <window.AhCheck /> Verified as <strong>{account.login}</strong>
                  </p>
                  {loadingRepos && <p className="ah-small">Loading your repositories…</p>}
                  {repos && repos.length === 0 && (
                    <p className="ah-small">
                      No repositories granted. Either none were selected during install,
                      or you have push access only — which lets you change code but not
                      commit the owner to a third party testing it. Grant the repo, or
                      paste a URL below and warrant it instead.
                    </p>
                  )}
                  {repos && repos.length > 0 && (
                    <React.Fragment>
                      <p className="ah-auth-label">Repositories you granted — choose one to scan</p>
                      <ul className="ah-repo-list">
                        {repos.map((r) => {
                          const cost = repoCost(r.fullName);
                          const on = picked && picked.id === r.id;
                          return (
                            <li key={r.id}>
                              <label className={`ah-repo ${on ? 'is-picked' : ''}`}>
                                <input
                                  type="radio" name="granted-repo" checked={!!on}
                                  onChange={() => { setPicked(r); setRepo(`github.com/${r.fullName}`); }}
                                />
                                <span className="ah-repo-name">
                                  {r.fullName}
                                  {r.private && <span className="ah-repo-tag">private</span>}
                                </span>
                                <span className={`ah-repo-cost is-${cost}`}>
                                  {cost === 'free' && 'Free'}
                                  {cost === 'authorized' && 'Already authorized'}
                                  {cost === 'credit' && 'Uses 1 credit'}
                                  {cost === 'subscription' && 'Included'}
                                  {cost === 'upgrade' && 'Needs upgrade'}
                                </span>
                              </label>
                            </li>
                          );
                        })}
                      </ul>

                      {/* The paywall surfaces the moment a second repository is
                          chosen, not after the form has been filled in and
                          signed. */}
                      {needsUpgrade && (
                        <div className="ah-repo-upsell">
                          <div>
                            <p className="ah-repo-upsell-t">
                              Your free scan is already used on another repository.
                            </p>
                            <p className="ah-small">
                              Add <strong>{picked.fullName}</strong> with a one-off Managed Scan,
                              or take Developer for unlimited repositories.
                            </p>
                          </div>
                          <a
                            className="ah-btn"
                            href={`/upgrade/?repo=${encodeURIComponent(`github.com/${picked.fullName}`)}`}
                            onClick={() => {
                              try { window.sessionStorage.setItem('sekura.grant', grant || ''); } catch { /* private mode */ }
                            }}
                          >
                            See the options
                          </a>
                        </div>
                      )}
                    </React.Fragment>
                  )}
                </div>
              )}

              <label className="ah-auth-label" htmlFor="auth-repo">Repository URL</label>
              <input
                id="auth-repo"
                className={`ah-auth-input ${attempted && !repoValid ? 'is-invalid' : ''}`}
                value={repo}
                onChange={(e) => {
                  const v = e.target.value;
                  setRepo(v);
                  // Keep the verified selection if they typed the same repo a
                  // different way; drop it if they pointed somewhere else.
                  const still = (repos || []).find((r) => sameRepo(v, r.fullName));
                  setPicked(still || null);
                }}
                placeholder="github.com/your-company/your-repo"
                spellCheck={false} autoCorrect="off" autoCapitalize="off"
              />
              {attempted && !repoValid && (
                <p className="ah-auth-err">
                  {(window.githubLib && window.githubLib.validate(repo).error) || 'Enter a GitHub, GitLab, or Bitbucket repository URL.'}
                </p>
              )}

              {!verified && (
                <React.Fragment>
                  <label className="ah-auth-check ah-auth-check--inline">
                    <input type="checkbox" checked={isPrivate} onChange={() => setIsPrivate((v) => !v)} />
                    <span>This is a private repository</span>
                  </label>
                  {isPrivate && (
                    <div className="ah-auth-private">
                      <label className="ah-auth-label" htmlFor="auth-token">Read-only clone token</label>
                      <input
                        id="auth-token" className="ah-auth-input" type="password"
                        value={repoToken} onChange={(e) => setRepoToken(e.target.value)}
                        placeholder="ghp_… / glpat-…" autoComplete="off" spellCheck={false}
                      />
                      <p className="ah-caption ah-auth-hint">
                        Held only between queueing and completion, then erased. Never logged.
                      </p>
                    </div>
                  )}
                </React.Fragment>
              )}
            </section>

            <section className="ah-auth-block">
              <h2 className="ah-h3">The authorization</h2>
              <p className="ah-body ah-auth-note">
                One agreement, covering all of the following. It mirrors{' '}
                <a className="ah-link" href="/terms/">{clauseRefs}</a>.
              </p>

              <ol className="ah-auth-terms">
                {terms.map((t) => (
                  <li key={t.k}>
                    <span className="ah-auth-letter">{t.k})</span>
                    <span>{t.label}</span>
                  </li>
                ))}
              </ol>

              <label className={`ah-auth-check ah-auth-agree ${attempted && !agreed ? 'is-missing' : ''}`}>
                <input type="checkbox" checked={agreed} onChange={() => setAgreed((v) => !v)} />
                <span>
                  <strong>I confirm (a) through (g) above</strong> in respect of the repository
                  named on this page, and I am authorized to give this confirmation on behalf
                  of its owner.
                </span>
              </label>
            </section>

            <section className="ah-auth-block">
              <h2 className="ah-h3">Signature</h2>
              <p className="ah-body ah-auth-note">
                An authorization is only worth something if it names who gave it.
              </p>
              <div className="ah-auth-fields">
                <div>
                  <label className="ah-auth-label" htmlFor="auth-name">Full name</label>
                  <input id="auth-name" className={`ah-auth-input ${attempted && !name.trim() ? 'is-invalid' : ''}`}
                    value={name} onChange={(e) => setName(e.target.value)} autoComplete="name" />
                </div>
                <div>
                  <label className="ah-auth-label" htmlFor="auth-title">Job title <span className="ah-auth-opt">optional</span></label>
                  <input id="auth-title" className="ah-auth-input" value={title}
                    onChange={(e) => setTitle(e.target.value)} autoComplete="organization-title" />
                </div>
                <div>
                  <label className="ah-auth-label" htmlFor="auth-org">Organization <span className="ah-auth-opt">optional</span></label>
                  <input id="auth-org" className="ah-auth-input" value={org}
                    onChange={(e) => setOrg(e.target.value)} autoComplete="organization" />
                </div>
                <div>
                  <label className="ah-auth-label" htmlFor="auth-email">Work email</label>
                  <input id="auth-email" type="email"
                    className={`ah-auth-input ${attempted && !/.+@.+\..+/.test(email) ? 'is-invalid' : ''}`}
                    value={email} onChange={(e) => setEmail(e.target.value)} autoComplete="email" />
                </div>
              </div>

              <label className="ah-auth-label" htmlFor="auth-sign">Type your full name to sign</label>
              <input
                id="auth-sign"
                className={`ah-auth-input ah-auth-signature ${attempted && signature.trim().toLowerCase() !== name.trim().toLowerCase() ? 'is-invalid' : ''}`}
                value={signature} onChange={(e) => setSignature(e.target.value)}
                placeholder={name.trim() || 'Your full name'} autoComplete="off"
              />

              <label className="ah-auth-label">Signature</label>
              <div className={attempted && !drawn ? 'ah-sig--missing' : ''}>
                <window.SignaturePad onChange={setDrawn} />
              </div>
              <p className="ah-caption ah-auth-hint">
                We record both signatures with your IP address, the time, how the mark was
                drawn, and a fingerprint of the exact wording shown on this page — then seal
                the whole record so it can be shown later to be unaltered, including by us.
              </p>

              <label className="ah-auth-check ah-auth-check--inline">
                <input type="checkbox" checked={featureConsent} onChange={() => setFeatureConsent((v) => !v)} />
                <span>Sekura may mention this organization as a scanned project <span className="ah-auth-opt">optional</span></span>
              </label>

              {/* Honeypot — hidden from people, irresistible to bots. */}
              <input ref={hpRef} type="text" name="company_website" tabIndex={-1}
                autoComplete="off" aria-hidden="true" className="ah-auth-hp" />
            </section>

            {error && <p className="ah-auth-err ah-auth-err--form">{error}</p>}

            <div className="ah-auth-submit">
              <button type="submit" className="ah-btn" disabled={submitting}>
                {submitting ? 'Submitting…' : 'Authorize and start the scan'}
              </button>
              {attempted && firstMissing && (
                <p className="ah-auth-err">Still needed: {firstMissing}.</p>
              )}
              {!attempted && (
                <p className="ah-caption">
                  By signing you accept the <a className="ah-link" href="/terms/">Terms of Service</a> and{' '}
                  <a className="ah-link" href="/privacy/">Privacy Policy</a>.
                </p>
              )}
            </div>
          </div>

          <aside className="ah-auth-side">
            <div className="ah-auth-card">
              <h2 className="ah-eyebrow">What we will not do</h2>
              <ul className="ah-auth-roe">
                {roe.map((r) => (<li key={r}><window.AhCheck />{r}</li>))}
              </ul>
              <p className="ah-caption ah-auth-hint">
                These are the rules of engagement we hold ourselves to on every scan,
                paid or free.
              </p>
            </div>
          </aside>
        </div>
      </form>
    </div>
  );
}

window.AuthorizeView = AuthorizeView;
