/* ELEV8 ATS - R107: candidate self-service portal (chrome-free, mobile-first).
   A candidate arrives via a (simulated) magic link at #/portal/:candId, verifies/updates their
   profile, and it flows back to the ATS via the R106 receiver (window.applyCandidateVerification),
   which does ALL of the flow-back (freshness reset, own-truth auto-apply + provenance, assessive
   conflict flagging). This file is the landing (Task 1) + the real verify FORM + confirmation (Task 2).
   NEVER build real auth here - the magic-link + passkey entry are simulated (no password field,
   no backend). Rendered by main.jsx OUTSIDE the app shell: no sidebar / topbar / tab bar / cmdk. */

const DSp = window.Stand8DesignSystem_b5c975;

/* Mask a candidate's email for the "we sent a secure link to ..." reassurance line: keep the first
   character of the local part + the full domain (bia.silva.eng@gmail.com -> b•••@gmail.com). The
   full address is never rendered on the landing. */
function e8MaskEmail(email) {
  if (!email || typeof email !== 'string' || email.indexOf('@') < 1) return 'your email on file';
  const parts = email.split('@');
  const head = (parts[0] || '').slice(0, 1);
  return (head || '•') + '•••@' + parts[1];
}

function e8PortalFirst(name) { return (name || '').split(' ')[0] || 'there'; }

/* Recap formatting for the timeline note + confirmation. Booleans read as Yes/No; arrays join. */
const E8_PORTAL_LABEL = {
  location: 'Location', rate: 'Rate', availability: 'Availability', email: 'Email',
  phone: 'Phone', consent: 'Talent network', skills: 'Skills', title: 'Title',
  workAuth: 'Work authorization',
};
function e8PortalLabel(f) { return E8_PORTAL_LABEL[f] || f; }
function e8PortalVal(v) {
  if (v === true) return 'Yes';
  if (v === false) return 'No';
  if (v == null || v === '' || (Array.isArray(v) && !v.length)) return '-';
  return Array.isArray(v) ? v.join(', ') : String(v);
}
function e8SameSkills(a, b) {
  a = a || []; b = b || [];
  if (a.length !== b.length) return false;
  for (let i = 0; i < a.length; i++) if (String(a[i]) !== String(b[i])) return false;
  return true;
}

/* Availability + work-authorization option sets. Availability values reuse the EXISTING candidateMeta
   vocabulary (Immediate | 2 weeks | 1 month | Passive) so the record + list facet stay coherent - only
   the labels are candidate-friendly. Work authorization mirrors the application-form field's options. */
const E8_PORTAL_AVAIL = [
  { value: '', label: 'Select availability' },
  { value: 'Immediate', label: 'Available immediately' },
  { value: '2 weeks', label: "Two weeks' notice" },
  { value: '1 month', label: 'One month notice' },
  { value: 'Passive', label: 'Open, but not actively looking' },
];
const E8_PORTAL_WORKAUTH = [
  { value: '', label: 'Select work authorization' },
  { value: 'US Citizen', label: 'US citizen' },
  { value: 'Green Card', label: 'Green card' },
  { value: 'H-1B', label: 'H-1B' },
  { value: 'OPT/CPT', label: 'OPT / CPT' },
  { value: 'TN', label: 'TN visa' },
  { value: 'Other', label: 'Other / prefer to discuss' },
];
const E8_PORTAL_CONTACT = [
  { value: 'Email', label: 'Email' },
  { value: 'Phone', label: 'Phone call' },
  { value: 'Text', label: 'Text message' },
];

/* Shared brand lockup for the portal (the "8" mark + STAND8 wordmark). */
function PortalBrand() {
  return (
    <div className="e8-cportal-brand">
      <span className="e8-cportal-logomark" aria-hidden="true">8</span>
      <span className="e8-cportal-wordmark">STAND8</span>
    </div>
  );
}

/* Stage: landing - the simulated passwordless magic-link entry. Continue simulates clicking the
   emailed link; "Use a passkey" is a simulated ceremony. Neither is real auth - there is no
   password field and no backend; both simply advance into the portal. */
function PortalLanding({ cand, onEnter }) {
  const masked = e8MaskEmail(cand.email);
  return (
    <div className="e8-cportal-card">
      <PortalBrand />
      <span className="material-symbols-outlined e8-cportal-glyph" aria-hidden="true">mark_email_read</span>
      <h1 className="e8-cportal-title">Verify your profile with STAND8</h1>
      <p className="e8-cportal-sub">
        We sent a secure link to <b className="e8-cportal-email">{masked}</b>. Confirm your details so we
        can match you to the right roles - it takes about a minute.
      </p>
      <DSp.Button variant="primary" size="lg" icon="arrow_forward" className="e8-cportal-cta" onClick={onEnter}>
        Continue
      </DSp.Button>
      <button type="button" className="e8-cportal-passkey" onClick={onEnter}>
        <span className="material-symbols-outlined" aria-hidden="true">fingerprint</span>
        Use a passkey instead
      </button>
      <p className="e8-cportal-fine">
        This confirms it is really you. STAND8 will never ask for a password on this page.
      </p>
    </div>
  );
}

/* A labelled portal section (a titled group of fields). */
function PortalSection({ icon, title, hint, children }) {
  return (
    <section className="e8-cportal-section">
      <div className="e8-cportal-section-head">
        <span className="material-symbols-outlined e8-cportal-section-icon" aria-hidden="true">{icon}</span>
        <span className="e8-cportal-section-title">{title}</span>
      </div>
      {hint ? <p className="e8-cportal-section-hint">{hint}</p> : null}
      <div className="e8-cportal-fields">{children}</div>
    </section>
  );
}

/* Stage: form - the real verify form, pre-filled from the candidate's CURRENT values (D.matches row +
   candidateMeta). On submit it diffs against those current values (so unchanged fields are not spuriously
   sent - unchanged assessive skills/title never become phantom conflicts) and calls the R106 receiver;
   an "everything's correct" submit sends an empty diff, which still resets freshness. */
function PortalForm({ cand, meta, onDone, onBack }) {
  const first = e8PortalFirst(cand.name);
  const submittedRef = React.useRef(false);
  const fileRef = React.useRef(null);

  const baseAvail = meta.availability || '';
  const baseConsent = !!meta.consent;

  const [email, setEmail] = React.useState(cand.email || '');
  const [phone, setPhone] = React.useState(cand.phone || '');
  const [location, setLocation] = React.useState(cand.location || '');
  const [rate, setRate] = React.useState(cand.rate || '');
  const [availability, setAvailability] = React.useState(baseAvail);
  const [workAuth, setWorkAuth] = React.useState(meta.workAuth || '');
  const [title, setTitle] = React.useState(cand.title || '');
  const [skills, setSkills] = React.useState((cand.skills || []).slice());
  const [skillDraft, setSkillDraft] = React.useState('');
  const [consent, setConsent] = React.useState(baseConsent);
  const [contactPref, setContactPref] = React.useState(meta.contactPref || 'Email');
  const [resumeName, setResumeName] = React.useState('');

  /* Availability options - include the candidate's current value if it is outside the standard set so the
     Select still shows it selected rather than silently falling back to the placeholder. Memoized on
     baseAvail so it isn't rebuilt every render. */
  const availOptions = React.useMemo(() => (
    E8_PORTAL_AVAIL.some((o) => o.value === baseAvail) || !baseAvail
      ? E8_PORTAL_AVAIL
      : E8_PORTAL_AVAIL.concat([{ value: baseAvail, label: baseAvail }])
  ), [baseAvail]);

  const addSkill = () => {
    const s = skillDraft.trim();
    if (s && skills.indexOf(s) === -1) setSkills(skills.concat([s]));
    setSkillDraft('');
  };
  const removeSkill = (s) => setSkills(skills.filter((x) => x !== s));
  const onSkillKey = (e) => {
    if (e.key === 'Enter') { e.preventDefault(); addSkill(); }
    else if (e.key === ',') { e.preventDefault(); addSkill(); }
  };

  const onResumePick = (file) => {
    if (!file) return;
    /* Honest handling: we capture the filename and note that the recruiter will refresh from the resume.
       No fake parse / fabricated success - the resume does not flow through the structured receiver. */
    setResumeName(file.name || 'resume');
  };

  const dirty = (email.trim() !== (cand.email || '')) ||
    (phone.trim() !== (cand.phone || '')) ||
    (location.trim() !== (cand.location || '')) ||
    (rate.trim() !== (cand.rate || '')) ||
    (availability !== baseAvail) ||
    (title.trim() !== (cand.title || '')) ||
    !e8SameSkills(skills, cand.skills || []) ||
    (consent !== baseConsent) ||
    (workAuth !== (meta.workAuth || '')) ||
    (contactPref !== (meta.contactPref || 'Email')) ||
    !!resumeName;

  const submit = () => {
    if (submittedRef.current) return;

    /* Build `updates` from CHANGED receiver fields only (own-truth: email/phone/location/rate/
       availability/consent/workAuth; assessive: title/skills). Unchanged fields are omitted so the receiver
       neither re-stamps them nor flags unchanged skills/title as phantom conflicts. */
    const updates = {};
    const em = email.trim(), ph = phone.trim(), lo = location.trim(), ra = rate.trim(), ti = title.trim();
    if (em !== (cand.email || '')) updates.email = em;
    if (ph !== (cand.phone || '')) updates.phone = ph;
    if (lo !== (cand.location || '')) updates.location = lo;
    if (ra !== (cand.rate || '')) updates.rate = ra;
    if (availability !== baseAvail) updates.availability = availability;
    if (workAuth !== (meta.workAuth || '')) updates.workAuth = workAuth;
    if (ti !== (cand.title || '')) updates.title = ti;
    if (!e8SameSkills(skills, cand.skills || [])) updates.skills = skills.slice();
    if (consent !== baseConsent) updates.consent = consent;

    const res = (typeof window.applyCandidateVerification === 'function')
      ? window.applyCandidateVerification(cand.id, updates, { source: 'candidate' })
      : { applied: [], conflicts: [] };
    const applied = res.applied || [];
    const conflicts = res.conflicts || [];

    /* Captured extras with no structured receiver field (contact preference, a re-uploaded resume) -
       surfaced to the recruiter on the timeline note only, never faked as structured writes. Work
       authorization is now a real own-truth field handled by the receiver (appears in `applied`), so it is
       NOT re-listed here - that would double it on the note + confirmation recap. */
    const extras = [];
    if (contactPref !== (meta.contactPref || 'Email')) extras.push('Preferred contact: ' + contactPref);
    if (resumeName) extras.push('Uploaded an updated resume');

    /* Timeline event: the receiver deliberately does NOT write it, so the portal records one replayable
       note (E8Store.add - survives reload) from the returned diff, mirroring the R106 flow. */
    const points = [];
    applied.forEach((a) => points.push(e8PortalLabel(a.field) + ': ' + e8PortalVal(a['new'])));
    conflicts.forEach((cf) => points.push(e8PortalLabel(cf.field) + ' flagged for review (' + e8PortalVal(cf.old) + ' -> ' + e8PortalVal(cf['new']) + ')'));
    extras.forEach((x) => points.push(x));
    if (window.E8Store) window.E8Store.add('notes', {
      id: 'n-cv-' + Date.now().toString(36),
      title: 'Candidate verified their profile',
      points: points.length ? points : ['Confirmed profile - no field changes'],
      refType: 'candidate', refId: cand.id, ts: Date.now(),
      author: cand.name, prov: 'human', when: 'Just now',
    });

    submittedRef.current = true;
    onDone({ applied: applied, conflicts: conflicts, extras: extras, resumeName: resumeName });
  };

  return (
    <div className="e8-cportal-card e8-cportal-form">
      <PortalBrand />
      <div className="e8-cportal-lede">
        <h1 className="e8-cportal-title e8-cportal-title-left">Your candidate profile</h1>
        <p className="e8-cportal-sub e8-cportal-sub-left">
          Hi {first} - keeping this current helps us match you to the right roles. Review each detail and
          update anything that has changed. Takes about a minute.
        </p>
      </div>

      <PortalSection icon="mail" title="Contact">
        <DSp.Field label="Email">
          <DSp.Input type="email" inputMode="email" autoComplete="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@example.com" />
        </DSp.Field>
        <DSp.Field label="Phone">
          <DSp.Input type="tel" inputMode="tel" autoComplete="tel" value={phone} onChange={(e) => setPhone(e.target.value)} placeholder="(555) 555-0100" />
        </DSp.Field>
      </PortalSection>

      <PortalSection icon="location_on" title="Location">
        <DSp.Field label="City, state">
          <DSp.Input value={location} onChange={(e) => setLocation(e.target.value)} placeholder="City, State" />
        </DSp.Field>
      </PortalSection>

      <PortalSection icon="payments" title="Compensation">
        <DSp.Field label="Target rate or salary" help="For example $95/hr or $150k">
          <DSp.Input value={rate} onChange={(e) => setRate(e.target.value)} placeholder="$95/hr" />
        </DSp.Field>
      </PortalSection>

      <PortalSection icon="event_available" title="Availability">
        <DSp.Field label="When could you start?">
          <DSp.Select value={availability} onChange={(e) => setAvailability(e.target.value)} options={availOptions} />
        </DSp.Field>
      </PortalSection>

      <PortalSection icon="verified_user" title="Work authorization">
        <DSp.Field label="Status">
          <DSp.Select value={workAuth} onChange={(e) => setWorkAuth(e.target.value)} options={E8_PORTAL_WORKAUTH} />
        </DSp.Field>
      </PortalSection>

      <PortalSection icon="sell" title="Skills" hint="Add or remove so recruiters see what you do best.">
        <div className="e8-cportal-chips">
          {skills.length
            ? skills.map((s) => (
                <span key={s} className="e8-cportal-chip">
                  {s}
                  <button type="button" className="e8-cportal-chip-x" onClick={() => removeSkill(s)} title={'Remove ' + s} aria-label={'Remove ' + s}>
                    <span className="material-symbols-outlined" aria-hidden="true">close</span>
                  </button>
                </span>
              ))
            : <span className="e8-cportal-chips-empty">No skills yet - add a few below.</span>}
        </div>
        <div className="e8-cportal-skilladd">
          <DSp.Input value={skillDraft} onChange={(e) => setSkillDraft(e.target.value)} onKeyDown={onSkillKey} placeholder="Add a skill" wrapStyle={{ flex: 1 }} />
          <DSp.Button variant="secondary" icon="add" onClick={addSkill}>Add</DSp.Button>
        </div>
      </PortalSection>

      <PortalSection icon="badge" title="Current title">
        <DSp.Field label="Your current role">
          <DSp.Input value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Sr. Software Engineer" />
        </DSp.Field>
      </PortalSection>

      <PortalSection icon="description" title="Resume">
        <input ref={fileRef} type="file" accept=".pdf,.doc,.docx,.txt,.rtf" style={{ display: 'none' }}
          onChange={(e) => { onResumePick(e.target.files && e.target.files[0]); e.target.value = ''; }} />
        <button type="button" className="e8-cportal-filebtn" onClick={() => fileRef.current && fileRef.current.click()}>
          <span className="material-symbols-outlined" aria-hidden="true">upload_file</span>
          {resumeName ? 'Choose a different file' : 'Attach an updated resume'}
        </button>
        {resumeName
          ? <p className="e8-cportal-filenote"><span className="material-symbols-outlined" aria-hidden="true">check_circle</span>Attached <b>{resumeName}</b> - we'll refresh your profile from it.</p>
          : <p className="e8-cportal-section-hint">Optional. PDF or Word. Your recruiter will refresh your profile from the latest copy.</p>}
      </PortalSection>

      <PortalSection icon="handshake" title="Staying in touch">
        <label className="e8-cportal-check">
          <DSp.Checkbox checked={consent} onChange={(e) => setConsent(e.target.checked)} />
          <span>Keep me in the STAND8 talent network for future roles.</span>
        </label>
        <DSp.Field label="Preferred way to reach you">
          <DSp.Select value={contactPref} onChange={(e) => setContactPref(e.target.value)} options={E8_PORTAL_CONTACT} />
        </DSp.Field>
      </PortalSection>

      <div className="e8-cportal-actions">
        <DSp.Button variant="primary" size="lg" icon={dirty ? 'send' : 'check'} className="e8-cportal-cta" onClick={submit}>
          {dirty ? 'Submit updates' : "Everything's still correct"}
        </DSp.Button>
        <p className="e8-cportal-hint">
          {dirty ? 'We only send what you changed.' : 'Confirms your profile is current and up to date.'}
        </p>
        <button type="button" className="e8-cportal-textlink" onClick={onBack}>Back</button>
      </div>
    </div>
  );
}

/* Stage: done - the confirmation. Recap comes from the receiver's returned diff (`applied`) with any
   assessive conflicts phrased neutrally (the recruiter reviews those). Chrome-free. */
function PortalDone({ cand, result }) {
  const first = e8PortalFirst(cand.name);
  const today = new Date().toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
  const applied = (result && result.applied) || [];
  const conflicts = (result && result.conflicts) || [];
  const extras = (result && result.extras) || [];
  const nothing = !applied.length && !conflicts.length && !extras.length;

  return (
    <div className="e8-cportal-card">
      <PortalBrand />
      <span className="material-symbols-outlined e8-cportal-glyph e8-cportal-glyph-ok" aria-hidden="true">check_circle</span>
      <h1 className="e8-cportal-title">Thanks, {first}</h1>
      <p className="e8-cportal-sub">Your profile is up to date. Verified {today}.</p>

      <div className="e8-cportal-recap">
        {nothing ? (
          <p className="e8-cportal-recap-note">We've confirmed everything on your profile is current - nothing needed changing.</p>
        ) : (
          <React.Fragment>
            {applied.length ? (
              <React.Fragment>
                <div className="e8-cportal-recap-title">What we updated</div>
                <ul className="e8-cportal-recap-list">
                  {applied.map((a) => (
                    <li key={a.field} className="e8-cportal-recap-item">
                      <span className="e8-cportal-recap-field">{e8PortalLabel(a.field)}</span>
                      <span className="e8-cportal-recap-val">{e8PortalVal(a['new'])}</span>
                    </li>
                  ))}
                  {extras.map((x, i) => (
                    <li key={'x' + i} className="e8-cportal-recap-item e8-cportal-recap-item-note">{x}</li>
                  ))}
                </ul>
              </React.Fragment>
            ) : extras.length ? (
              <ul className="e8-cportal-recap-list">
                {extras.map((x, i) => (
                  <li key={'x' + i} className="e8-cportal-recap-item e8-cportal-recap-item-note">{x}</li>
                ))}
              </ul>
            ) : null}
            {conflicts.length ? (
              <p className="e8-cportal-recap-note">
                Our team will review {conflicts.length === 1 ? 'one update' : 'a couple of updates'} before {conflicts.length === 1 ? 'it goes' : 'they go'} live.
              </p>
            ) : null}
          </React.Fragment>
        )}
      </div>

      <p className="e8-cportal-fine">You can close this page. Your STAND8 recruiter can now see your latest details.</p>
    </div>
  );
}

/* Not-found guard: an unknown / expired candidate id resolves to a friendly dead-link screen. */
function PortalInvalid() {
  return (
    <div className="e8-cportal-card" role="alert">
      <PortalBrand />
      <span className="material-symbols-outlined e8-cportal-glyph e8-cportal-glyph-muted" aria-hidden="true">link_off</span>
      <h1 className="e8-cportal-title">This link is no longer valid</h1>
      <p className="e8-cportal-sub">
        Your secure link may have expired or already been used. Ask your STAND8 recruiter to send a fresh one.
      </p>
    </div>
  );
}

/* CandidatePortal: the chrome-free candidate surface. `stage` walks landing -> form -> done.
   Current values come from D.matches[id] (profile row) + candidateMeta[id] (availability/consent). */
function CandidatePortal({ id, sub }) {
  const D = window.E8DATA || {};
  const cand = (D.matches || []).find((m) => m.id === id) || null;
  const meta = ((D.candidateMeta || {})[id]) || {};
  const [stage, setStage] = React.useState('landing');
  const [result, setResult] = React.useState(null);

  /* Reset to the landing whenever the candidate id changes. A real magic link is a fresh page load, but
     an in-app hash-only navigation between portal ids (e.g. the recruiter "Preview candidate portal"
     deep-link) reuses this component instance - without this, the prior candidate's stage/result would
     carry over. */
  React.useEffect(() => { setStage('landing'); setResult(null); }, [id]);

  const onDone = (res) => { setResult(res); setStage('done'); };

  let body;
  if (!cand) body = <PortalInvalid />;
  else if (stage === 'form') body = <PortalForm cand={cand} meta={meta} onDone={onDone} onBack={() => setStage('landing')} />;
  else if (stage === 'done') body = <PortalDone cand={cand} result={result} />;
  else body = <PortalLanding cand={cand} onEnter={() => setStage('form')} />;

  const tall = cand && stage === 'form';
  return <div className={'e8-cportal' + (tall ? ' e8-cportal-tall' : '')}>{body}</div>;
}

Object.assign(window, { CandidatePortal, e8MaskEmail });
