// ForestLynk — Claim-your-handle screen.
// First-time login only; landed here from the magic-link callback when the API
// returns handle: null. Single-step claim, with debounced uniqueness check.

const HANDLE_MAX = 30;
const HANDLE_MIN = 3;
const HANDLE_RE_OK = /^[a-z0-9](?:[a-z0-9]|[-_](?![-_]))*[a-z0-9]$/;
const RESERVED = new Set(['admin', 'forestlynk', 'you', 'me', 'system', 'support']);
const TAKEN_SAMPLE = new Set(['acorn-37', 'marina_k', 'samh', 'fern-12', 'pine_92', 'forest', 'oakling', 'birch-7']);

const IMMUTABILITY_COPY = {
  terse:    "This can't be changed later.",
  warm:     "Choose carefully — handles are permanent.",
  observational: "You'll keep this handle as long as you're on ForestLynk.",
};

const WHAT_IS_HANDLE_COPY = {
  short:  "Others see this everywhere you show up in the forest.",
  longer: "It's how every node, member chip, and grove row will name you.",
  plain:  "Your name across ForestLynk — on nodes, chips, and member lists.",
};

// validate(value) → one of:
//   'empty' | 'too-short' | 'too-long' | 'edge' | 'consec' | 'char' | 'reserved' | 'ok'
function validateHandle(raw) {
  const v = (raw || '').toLowerCase();
  if (v.length === 0) return 'empty';
  if (v.length < HANDLE_MIN) return 'too-short';
  if (v.length > HANDLE_MAX) return 'too-long';
  if (/^[-_]|[-_]$/.test(v)) return 'edge';
  if (/[-_]{2}/.test(v) || /-_|_-/.test(v)) return 'consec';
  if (!/^[a-z0-9_-]+$/.test(v)) return 'char';
  if (RESERVED.has(v) || v.startsWith('__')) return 'reserved';
  return 'ok';
}

const ERR_COPY = {
  'too-short': `At least ${HANDLE_MIN} characters.`,
  'too-long':  `${HANDLE_MAX} characters is the ceiling.`,
  'edge':      "Can't start or end with - or _.",
  'consec':    "No back-to-back separators.",
  'char':      "Lowercase letters, numbers, hyphen, underscore.",
  'reserved':  "That one's reserved.",
};

// ClaimHandleScreen — pure, state-driven.
//
// props.checkState ∈ 'idle' | 'checking' | 'available' | 'taken' | 'server-error'
// props.status ∈ 'idle' | 'submitting' | 'success'   (top-level submit lifecycle)
function ClaimHandleScreen({
  handle = '',
  onChange,
  onSubmit,
  onSuggest,
  onCancel,
  checkState = 'idle',
  status = 'idle',
  errorMessage = null,
  monospace = true,
  suggestEnabled = true,
  immutabilityCopy = IMMUTABILITY_COPY.observational,
  whatIsHandleCopy = WHAT_IS_HANDLE_COPY.short,
  focused = false,
  variant = 'canonical', // canonical | grounded | meter
}) {
  const validity = validateHandle(handle);
  const valid = validity === 'ok';
  const showHelper = handle.length === 0 || valid;
  const isSubmitting = status === 'submitting';

  // Field state derived from both client validity and server check.
  const inputState =
    !valid && handle.length >= 3 ? 'invalid' :
    checkState === 'taken'        ? 'invalid' :
    checkState === 'server-error' ? 'invalid' :
    checkState === 'available'    ? 'available' :
                                    'idle';

  const inlineMessage =
    !valid && validity !== 'empty' && handle.length >= 1 ? ERR_COPY[validity] || null :
    checkState === 'taken'                ? "That handle is taken. Try another." :
    checkState === 'server-error'         ? "Couldn't check that one. Try again." :
    null;

  const ctaDisabled = !valid || checkState === 'checking' || checkState === 'taken' || isSubmitting;

  return (
    <div style={{
      position: 'absolute', inset: 0,
      background: '#0B1306', overflow: 'hidden',
      display: 'flex', flexDirection: 'column',
      padding: '0 28px',
    }}>
      {/* Small back-step affordance — not the magic-link surface */}
      <div style={{ paddingTop: 10, paddingBottom: 8, display: 'flex' }}>
        <button onClick={onCancel} style={{
          background: 'transparent', border: 'none', color: '#586552', padding: 4,
          font: '500 13px var(--font-sans)', cursor: 'pointer',
        }} aria-label="Sign out">Sign out</button>
        <span style={{ flex: 1 }}/>
        <span style={{
          font: '500 10px var(--font-sans)', color: '#5A6450',
          letterSpacing: '0.08em', textTransform: 'uppercase',
        }}>Step 2 of 2</span>
      </div>

      {/* Hero — calmer than the sign-in surface. Eyebrow + h2 + sub. */}
      <div style={{
        flexShrink: 0,
        paddingTop: '12%', maxWidth: 330,
        display: 'flex', flexDirection: 'column', gap: 12,
      }}>
        <span style={{
          font: '500 10.5px var(--font-sans)', color: '#76AB78',
          letterSpacing: '0.10em', textTransform: 'uppercase',
        }}>Final step</span>
        <h2 style={{
          font: '500 italic 30px/1.15 var(--font-serif)', color: '#F2EDE4',
          margin: 0, letterSpacing: '-0.02em',
        }}>
          Pick your <span style={{ fontStyle: 'normal', fontFamily: 'var(--font-sans)', fontWeight: 500 }}>handle</span>.
        </h2>
        <p style={{
          font: '400 14px/1.55 var(--font-sans)', color: '#8A9678', margin: 0, maxWidth: 310,
        }}>
          {whatIsHandleCopy}
        </p>
      </div>

      {/* Field block */}
      <div style={{
        paddingTop: 28, display: 'flex', flexDirection: 'column', gap: 10, minHeight: 0,
      }}>
        <FLField
          value={handle}
          onChange={onChange}
          placeholder="acorn-37"
          state={inputState}
          focused={focused}
          mono={monospace}
          ariaLabel="Handle"
          leadIcon={<HandlePrefix/>}
          trailing={
            <HandleTrailing
              hasValue={handle.length > 0}
              len={handle.length}
              checkState={checkState}
              suggestEnabled={suggestEnabled}
              onSuggest={onSuggest}
            />
          }
        />

        {/* Helper / inline-error / availability cue */}
        <div style={{ minHeight: 18, display: 'flex', alignItems: 'center', gap: 8 }}>
          {inlineMessage ? (
            <FLHelper tone="error">{inlineMessage}</FLHelper>
          ) : checkState === 'available' ? (
            <FLHelper tone="good">
              <span style={{ color: '#A8C5A0' }}>✓ </span>
              Available. Yours if you want it.
            </FLHelper>
          ) : checkState === 'checking' ? (
            <FLHelper tone="tertiary">
              Checking…
            </FLHelper>
          ) : (
            <FLHelper tone="tertiary">
              Lowercase, numbers, hyphen, underscore. 3–30 characters.
            </FLHelper>
          )}
        </div>

        {/* Immutability cue — calm bark-toned, with adjacent lock glyph */}
        <ImmutabilityRow copy={immutabilityCopy} />

        {/* CTA */}
        <div style={{ paddingTop: 4 }}>
          <FLPrimaryButton onClick={onSubmit} disabled={ctaDisabled} loading={isSubmitting}>
            {isSubmitting ? 'Claiming…' : 'Claim handle'}
          </FLPrimaryButton>
        </div>
      </div>
    </div>
  );
}

// Subtle leading prefix — communicates this is an identifier.
function HandlePrefix() {
  return (
    <span style={{
      font: '500 15px var(--font-mono)', color: '#586552',
      paddingRight: 2, userSelect: 'none',
    }}>@</span>
  );
}

// Trailing area in the input — counter + state glyph + suggest button.
function HandleTrailing({ hasValue, len, checkState, suggestEnabled, onSuggest }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
      {/* Only show counter as we approach the limit */}
      {len > HANDLE_MAX - 8 && (
        <span style={{
          font: '400 11.5px var(--font-mono)',
          color: len > HANDLE_MAX ? '#E08C6E' : '#8A9678',
          letterSpacing: '-0.01em',
        }}>{len}/{HANDLE_MAX}</span>
      )}
      <CheckStateGlyph state={checkState}/>
      {!hasValue && suggestEnabled && (
        <button onClick={onSuggest} style={{
          background: 'transparent', border: '0.5px solid #2D3A22',
          padding: '5px 10px', borderRadius: 999,
          font: '500 11px var(--font-sans)', color: '#76AB78', cursor: 'pointer',
          display: 'inline-flex', alignItems: 'center', gap: 5, whiteSpace: 'nowrap',
        }}>
          <DiceGlyph/> Suggest
        </button>
      )}
    </div>
  );
}

function CheckStateGlyph({ state }) {
  if (state === 'checking') {
    return (
      <span style={{
        width: 12, height: 12, borderRadius: '50%',
        border: '1.5px solid rgba(168,197,160,0.35)',
        borderTopColor: '#A8C5A0',
        animation: 'fl-spin 720ms linear infinite',
        display: 'inline-block',
      }}/>
    );
  }
  if (state === 'available') {
    return (
      <svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true">
        <path d="M2 7.5 L 5.6 11 L 12 4" stroke="#A8C5A0" strokeWidth="1.7" fill="none" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
    );
  }
  if (state === 'taken') {
    return (
      <svg width="14" height="14" viewBox="0 0 14 14" aria-hidden="true">
        <circle cx="7" cy="7" r="6" stroke="#E08C6E" strokeWidth="1.5" fill="none"/>
        <path d="M4.5 7 H 9.5" stroke="#E08C6E" strokeWidth="1.5" strokeLinecap="round"/>
      </svg>
    );
  }
  return null;
}

function DiceGlyph() {
  // A tiny canopy-sprig glyph; visually says "give me one".
  return (
    <svg width="11" height="11" viewBox="0 0 12 12" aria-hidden="true">
      <path d="M6 1 V 11" stroke="#76AB78" strokeWidth="1" strokeLinecap="round"/>
      <path d="M6 5 Q 3 5 2 3" stroke="#76AB78" strokeWidth="1" fill="none" strokeLinecap="round"/>
      <path d="M6 7 Q 9 7 10 5" stroke="#76AB78" strokeWidth="1" fill="none" strokeLinecap="round"/>
    </svg>
  );
}

// Immutability row — small inline lock glyph + observational copy.
function ImmutabilityRow({ copy }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'flex-start', gap: 8,
      padding: '10px 12px',
      background: '#1A1308',   // private-chip-bg adjacent — warm bark earth
      border: '0.5px solid #2A1F12',
      borderRadius: 10,
      marginTop: 2,
    }}>
      <LockGlyph size={14}/>
      <p style={{
        font: '400 12.5px/1.5 var(--font-sans)', color: '#D6BFA8', margin: 0,
      }}>{copy}</p>
    </div>
  );
}

function LockGlyph({ size = 14 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" aria-hidden="true" style={{ flexShrink: 0, marginTop: 2 }}>
      <rect x="3" y="7" width="10" height="7" rx="1.5" stroke="#D6BFA8" strokeWidth="1.1" fill="none"/>
      <path d="M5.5 7 V 5 a 2.5 2.5 0 0 1 5 0 V 7" stroke="#D6BFA8" strokeWidth="1.1" fill="none" strokeLinecap="round"/>
    </svg>
  );
}

// ─────────────────────────── Confirm modal ───────────────────────────
// Slide-up sheet over the claim screen. Re-shows the chosen handle in big
// type so the user can read what they're about to immortalize.
function ConfirmClaimModal({ handle, onConfirm, onCancel, loading = false, monospace = true }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 80,
      display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
    }}>
      <div onClick={onCancel} style={{
        position: 'absolute', inset: 0, background: 'rgba(7,16,10,0.72)',
        backdropFilter: 'blur(2px)', WebkitBackdropFilter: 'blur(2px)',
      }}/>
      <div style={{
        position: 'relative', background: '#111D0A',
        borderRadius: '24px 24px 0 0', padding: '14px 22px 22px',
        boxShadow: '0 -8px 40px rgba(0,0,0,0.55)',
        border: '0.5px solid #2D3A22', borderBottom: 'none',
      }}>
        <div style={{
          width: 36, height: 4, background: '#445239', borderRadius: 2,
          margin: '0 auto 16px',
        }}/>
        <div style={{
          font: '500 10.5px var(--font-sans)', color: '#76AB78',
          letterSpacing: '0.10em', textTransform: 'uppercase', marginBottom: 8,
        }}>You're claiming</div>
        <div style={{
          padding: '14px 16px', background: '#0B1306',
          border: '0.5px solid #2D3A22', borderRadius: 12,
          display: 'flex', alignItems: 'center', gap: 10,
          marginBottom: 14,
        }}>
          <span style={{ color: '#586552', font: `500 18px var(--font-mono)` }}>@</span>
          <span style={{
            font: `500 22px ${monospace ? "var(--font-mono)" : "var(--font-sans)"}`,
            color: '#F2EDE4', letterSpacing: '-0.01em', flex: 1, overflow: 'hidden', textOverflow: 'ellipsis',
          }}>{handle}</span>
          <LockGlyph size={16}/>
        </div>
        <p style={{
          font: '400 13.5px/1.55 var(--font-sans)', color: '#8A9678', margin: '0 0 16px',
        }}>
          This is how you'll show up on every node, chip, and grove row — for as long as you're on ForestLynk.
        </p>
        <div style={{ display: 'flex', gap: 10 }}>
          <button onClick={onCancel} disabled={loading} style={{
            flex: 1, padding: '13px', borderRadius: 12,
            background: 'transparent', border: '0.5px solid #445239',
            color: '#C9D2BB', font: '500 14px var(--font-sans)', cursor: 'pointer',
          }}>Pick again</button>
          <FLPrimaryButton onClick={onConfirm} disabled={loading} loading={loading} full={false}>
            <span style={{ paddingInline: 6 }}>{loading ? 'Claiming…' : 'Yes, claim it'}</span>
          </FLPrimaryButton>
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────── Success beat ───────────────────────────
// Brief 1-2s screen between handle claim and main tabs. Calm, no celebration.
function HandleSuccessBeat({ handle, monospace = true }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, background: '#0B1306',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      padding: 32, gap: 18,
    }}>
      <div style={{ position: 'relative', animation: 'fl-breathe 4.2s ease-in-out infinite' }}>
        <ThresholdMark size={72} pulse/>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
        <span style={{
          font: '500 10.5px var(--font-sans)', color: '#76AB78',
          letterSpacing: '0.10em', textTransform: 'uppercase',
        }}>Welcome</span>
        <div style={{
          font: `500 22px ${monospace ? "var(--font-mono)" : "var(--font-sans)"}`,
          color: '#F2EDE4', letterSpacing: '-0.01em',
        }}>@{handle}</div>
        <p style={{
          font: '400 italic 14.5px/1.5 var(--font-serif)', color: '#8A9678', margin: 0,
          textAlign: 'center', maxWidth: 260,
        }}>Walking you into the forest…</p>
      </div>
    </div>
  );
}

// ─────────────────────────── Welcome-back beat ───────────────────────────
// Returning user — deep-link return between callback and main tabs.
function WelcomeBackBeat({ handle = 'acorn-37', monospace = true }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, background: '#0B1306',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      padding: 32, gap: 14,
    }}>
      <div style={{ animation: 'fl-breathe 4.2s ease-in-out infinite' }}>
        <ThresholdMark size={64} pulse/>
      </div>
      <span style={{
        font: '500 10.5px var(--font-sans)', color: '#76AB78',
        letterSpacing: '0.10em', textTransform: 'uppercase',
      }}>Welcome back</span>
      <div style={{
        font: `500 22px ${monospace ? "var(--font-mono)" : "var(--font-sans)"}`,
        color: '#F2EDE4',
      }}>@{handle}</div>
      <p style={{
        font: '400 italic 14.5px/1.5 var(--font-serif)', color: '#586552', margin: 0,
        textAlign: 'center', maxWidth: 260,
      }}>Your forest is where you left it.</p>
    </div>
  );
}

// Breathing-glow ornament used by the threshold sign-in variant.
function BreathingGlow() {
  return (
    <div style={{
      position: 'absolute', top: '8%', left: '50%', transform: 'translate(-50%, 0)',
      width: 460, height: 460, pointerEvents: 'none',
      background: 'radial-gradient(circle at center, rgba(212,150,12,0.10) 0%, rgba(212,150,12,0.02) 35%, transparent 70%)',
      filter: 'blur(8px)',
      animation: 'fl-breathe-glow 4.2s ease-in-out infinite',
    }}/>
  );
}

Object.assign(window, {
  ClaimHandleScreen, ConfirmClaimModal, HandleSuccessBeat, WelcomeBackBeat,
  validateHandle, IMMUTABILITY_COPY, WHAT_IS_HANDLE_COPY, TAKEN_SAMPLE, HANDLE_MAX, HANDLE_MIN,
});
