// Reusable bits: cartoony SVG avatars, BNS wordmark, common UI.

const { useEffect, useRef, useState, useMemo } = React;

// ─── Cartoon Avatars ─────────────────────────────────────────────────────
// Each section gets a chunky cartoon character. Done as inline SVG so they
// inherit color via CSS. Big eyes, bold strokes, a little wonky on purpose.

// Expressions: a series of (eyes, mouth) drawings. Clicking the face
// cycles through them with a squish animation.
const EXPRESSIONS = ["open", "smile", "laugh", "wink", "stars", "love"];

function Mouth({ kind = "neutral", color }) {
  // mouth y baseline = 128
  switch (kind) {
    case "smile":
      return <path d="M70 124 Q100 156 130 124" stroke="#04061a" strokeWidth="6" fill="none" strokeLinecap="round" strokeLinejoin="round" />;
    case "laugh":
      return (
        <g>
          <path d="M68 120 Q100 162 132 120 Z" fill="#04061a" />
          <path d="M82 140 Q100 152 118 140" stroke={color || "#ff6b9d"} strokeWidth="4" fill="none" strokeLinecap="round" />
          <rect x="82" y="120" width="36" height="6" fill="#fff" />
        </g>
      );
    case "wink-mouth":
      return <path d="M65 130 Q100 155 135 130" stroke="#04061a" strokeWidth="6" fill="none" strokeLinecap="round" />;
    case "small":
      return <path d="M85 128 Q100 138 115 128" stroke="#04061a" strokeWidth="5" fill="none" strokeLinecap="round" />;
    case "o":
      return <ellipse cx="100" cy="130" rx="8" ry="11" fill="#04061a" />;
    case "tongue":
      return (
        <g>
          <path d="M70 122 Q100 156 130 122" stroke="#04061a" strokeWidth="6" fill="#04061a" strokeLinecap="round" />
          <ellipse cx="110" cy="148" rx="8" ry="10" fill="#ff6b9d" />
        </g>
      );
    case "neutral":
    default:
      return <path d="M80 130 L120 130" stroke="#04061a" strokeWidth="6" strokeLinecap="round" />;
  }
}

function Eyes({ expression = "open", cx1 = 70, cx2 = 130, cy = 95, r = 9 }) {
  if (expression === "open" || expression === "smile") {
    return (
      <g fill="#04061a">
        <circle cx={cx1} cy={cy} r={r} />
        <circle cx={cx2} cy={cy} r={r} />
        <circle cx={cx1 + 3} cy={cy - 3} r={r/3} fill="#fff"/>
        <circle cx={cx2 + 3} cy={cy - 3} r={r/3} fill="#fff"/>
      </g>
    );
  }
  if (expression === "laugh") {
    // happy closed eyes (curved arcs)
    return (
      <g stroke="#04061a" strokeWidth="5" fill="none" strokeLinecap="round">
        <path d={`M${cx1-10} ${cy+3} Q${cx1} ${cy-10} ${cx1+10} ${cy+3}`}/>
        <path d={`M${cx2-10} ${cy+3} Q${cx2} ${cy-10} ${cx2+10} ${cy+3}`}/>
      </g>
    );
  }
  if (expression === "wink") {
    return (
      <g fill="#04061a">
        <circle cx={cx1} cy={cy} r={r} />
        <circle cx={cx1 + 3} cy={cy - 3} r={r/3} fill="#fff"/>
        <path d={`M${cx2-12} ${cy} q12 -10 24 0`} stroke="#04061a" strokeWidth="4" fill="none" strokeLinecap="round" />
      </g>
    );
  }
  if (expression === "stars") {
    return (
      <g fill="#04061a">
        <path d={`M${cx1} ${cy-12} l3 9 9 3 -9 3 -3 9 -3 -9 -9 -3 9 -3z`} />
        <path d={`M${cx2} ${cy-12} l3 9 9 3 -9 3 -3 9 -3 -9 -9 -3 9 -3z`} />
      </g>
    );
  }
  if (expression === "love") {
    // heart eyes
    const heart = (cx) => (
      <path
        d={`M${cx} ${cy+8} C${cx-12} ${cy-4} ${cx-12} ${cy-14} ${cx-6} ${cy-14} C${cx-3} ${cy-14} ${cx} ${cy-10} ${cx} ${cy-7} C${cx} ${cy-10} ${cx+3} ${cy-14} ${cx+6} ${cy-14} C${cx+12} ${cy-14} ${cx+12} ${cy-4} ${cx} ${cy+8} Z`}
        fill="#ff3d7f"
      />
    );
    return <g>{heart(cx1)}{heart(cx2)}</g>;
  }
  return null;
}

function MOUTH_FOR(expression) {
  switch (expression) {
    case "open":   return "neutral";
    case "smile":  return "smile";
    case "laugh":  return "laugh";
    case "wink":   return "wink-mouth";
    case "stars":  return "small";
    case "love":   return "o";
    default:       return "neutral";
  }
}

// ─── Manga / anime face parts (Manga theme only) ─────────────────────────
// Big shōnen/shōjo-style shiny eyes, delicate features. Pure ink so it reads
// after the stage-wide grayscale filter.
function MangaEyes({ expression = "open", cx1 = 68, cx2 = 132, cy = 94 }) {
  const shiny = (cx) => (
    <g key={cx}>
      {/* upper lash line for depth */}
      <path d={`M${cx-13} ${cy-12} Q${cx} ${cy-18} ${cx+13} ${cy-12}`} stroke="#04061a" strokeWidth="4" fill="none" strokeLinecap="round"/>
      <ellipse cx={cx} cy={cy} rx="12" ry="16" fill="#04061a"/>
      {/* big top highlight + small lower glint = classic anime sparkle */}
      <circle cx={cx-4} cy={cy-6} r="5" fill="#fff"/>
      <circle cx={cx+4.5} cy={cy+5} r="2.4" fill="#fff"/>
    </g>
  );
  const happy = (cx) => (
    <path key={cx} d={`M${cx-12} ${cy-2} Q${cx} ${cy+12} ${cx+12} ${cy-2}`} stroke="#04061a" strokeWidth="5" fill="none" strokeLinecap="round"/>
  );
  const wink = (cx) => (
    <path key={cx} d={`M${cx-12} ${cy+2} Q${cx} ${cy-12} ${cx+12} ${cy+2}`} stroke="#04061a" strokeWidth="5" fill="none" strokeLinecap="round"/>
  );

  if (expression === "open" || expression === "smile") return <g>{shiny(cx1)}{shiny(cx2)}</g>;
  if (expression === "laugh") return <g>{happy(cx1)}{happy(cx2)}</g>;
  if (expression === "wink")  return <g>{shiny(cx1)}{wink(cx2)}</g>;
  if (expression === "stars") {
    const star = (cx) => (
      <g key={cx} fill="#04061a">
        <path d={`M${cx} ${cy-14} l3.5 10.5 10.5 3.5 -10.5 3.5 -3.5 10.5 -3.5 -10.5 -10.5 -3.5 10.5 -3.5z`}/>
        <circle cx={cx-3.5} cy={cy-3.5} r="2" fill="#fff"/>
      </g>
    );
    return <g>{star(cx1)}{star(cx2)}</g>;
  }
  if (expression === "love") {
    const heart = (cx) => (
      <g key={cx}>
        <path d={`M${cx} ${cy+9} C${cx-13} ${cy-5} ${cx-13} ${cy-16} ${cx-6.5} ${cy-16} C${cx-3} ${cy-16} ${cx} ${cy-11} ${cx} ${cy-8} C${cx} ${cy-11} ${cx+3} ${cy-16} ${cx+6.5} ${cy-16} C${cx+13} ${cy-16} ${cx+13} ${cy-5} ${cx} ${cy+9} Z`} fill="#04061a"/>
        <circle cx={cx-4} cy={cy-7} r="2.2" fill="#fff"/>
      </g>
    );
    return <g>{heart(cx1)}{heart(cx2)}</g>;
  }
  return null;
}

function MangaMouth({ kind = "neutral" }) {
  switch (kind) {
    case "smile":      return <path d="M88 131 Q100 142 112 131" stroke="#04061a" strokeWidth="4.5" fill="none" strokeLinecap="round"/>;
    case "laugh":      return (
      <g>
        <path d="M85 129 Q100 150 115 129 Z" fill="#04061a"/>
        <path d="M91 131 Q100 139 109 131" stroke="#fff" strokeWidth="2.6" fill="none" strokeLinecap="round"/>
      </g>
    );
    case "wink-mouth": return <path d="M89 132 Q100 141 111 132" stroke="#04061a" strokeWidth="4.5" fill="none" strokeLinecap="round"/>;
    case "small":      return <path d="M93 131 Q100 137 107 131" stroke="#04061a" strokeWidth="4" fill="none" strokeLinecap="round"/>;
    case "o":          return <ellipse cx="100" cy="134" rx="6" ry="8" fill="#04061a"/>;
    case "neutral":
    default:           return <path d="M93 132 L107 132" stroke="#04061a" strokeWidth="4.5" strokeLinecap="round"/>;
  }
}

// Classic manga blush — short parallel diagonal strokes on each cheek.
function MangaBlush() {
  return (
    <g stroke="#04061a" strokeWidth="2.6" strokeLinecap="round" opacity=".8" fill="none">
      <path d="M56 116 l9 6 M62 112 l9 6 M68 109 l9 6"/>
      <path d="M144 116 l-9 6 M138 112 l-9 6 M132 109 l-9 6"/>
    </g>
  );
}

function Avatar({ seed, color, size = 220, interactive = false, expression: expressionProp, defaultExpression = "open", onExpressionChange }) {
  const [expressionInternal, setExpressionInternal] = React.useState(expressionProp || defaultExpression);
  const expression = expressionProp !== undefined ? expressionProp : expressionInternal;
  const [squish, setSquish] = React.useState(false);

  // Manga theme renders a Japanese manga/anime-style face instead of the cartoon one
  const [manga, setManga] = React.useState(() => document.body.classList.contains('theme-manga'));
  React.useEffect(() => {
    const onTheme = (e) => setManga((e?.detail || (document.body.classList.contains('theme-manga') ? 'manga' : 'cinema')) === 'manga');
    onTheme();
    window.addEventListener('bns:themechange', onTheme);
    return () => window.removeEventListener('bns:themechange', onTheme);
  }, []);

  const cycle = (e) => {
    if (!interactive) return;
    setSquish(true);
    setTimeout(() => setSquish(false), 350);
    const next = EXPRESSIONS[(EXPRESSIONS.indexOf(expression) + 1) % EXPRESSIONS.length];
    if (expressionProp === undefined) setExpressionInternal(next);
    onExpressionChange?.(next);
    // Note: we intentionally do NOT stopPropagation here so the avatar can
    // live inside a clickable card and the parent still gets the click.
  };

  // seed-specific accessories sit on top of the face
  const accessory = (() => {
    switch (seed) {
      case "wave":
        // waving hand off to side
        return (
          <g transform="translate(155 50) rotate(15)" className="acc acc-wave">
            <rect x="-8" y="0" width="16" height="30" rx="8" fill="#04061a" />
            <circle cx="0" cy="-8" r="14" fill="#fff" stroke="#04061a" strokeWidth="4"/>
          </g>
        );
      case "tools":
        return (
          <g>
            <g transform="translate(40 40) rotate(-20)" className="acc acc-wrench">
              <rect x="-4" y="0" width="8" height="40" fill="#04061a" rx="2"/>
              <rect x="-10" y="-10" width="20" height="14" fill="#04061a" rx="2"/>
            </g>
            <g transform="translate(160 40) rotate(20)" className="acc acc-gear">
              <rect x="-4" y="0" width="8" height="40" fill="#04061a" rx="2"/>
              <circle cx="0" cy="-6" r="10" fill="none" stroke="#04061a" strokeWidth="4"/>
            </g>
          </g>
        );
      case "briefcase":
        return (
          <g transform="translate(100 165)" className="acc acc-bag">
            <rect x="-32" y="-2" width="64" height="34" rx="4" fill="#04061a"/>
            <rect x="-12" y="-12" width="24" height="14" rx="3" fill="none" stroke="#04061a" strokeWidth="4"/>
            <rect x="-4" y="12" width="8" height="4" fill="#fff" opacity=".5"/>
          </g>
        );
      case "rocket":
        return (
          <g transform="translate(100 30)" className="acc acc-rocket">
            <path d="M-12 10 Q0 -20 12 10 Z" fill="#04061a"/>
            <circle cx="0" cy="-2" r="4" fill="#fff"/>
            <path d="M-12 10 L-20 18 L-8 14 Z" fill="#04061a"/>
            <path d="M12 10 L20 18 L8 14 Z" fill="#04061a"/>
          </g>
        );
      case "envelope":
        return (
          <g transform="translate(100 175)" className="acc acc-env">
            <rect x="-36" y="-14" width="72" height="48" rx="4" fill="#fff" stroke="#04061a" strokeWidth="5"/>
            <path d="M-36 -14 L0 14 L36 -14" stroke="#04061a" strokeWidth="5" fill="none"/>
            <text x="0" y="28" textAnchor="middle" fontFamily="JetBrains Mono" fontSize="14" fontWeight="700" fill="#04061a">HEY!</text>
          </g>
        );
      case "doc":
        return (
          <g transform="translate(100 175)" className="acc acc-doc">
            <rect x="-26" y="-22" width="52" height="62" rx="4" fill="#fff" stroke="#04061a" strokeWidth="5"/>
            <path d="M-18 -10 L18 -10 M-18 -2 L18 -2 M-18 6 L10 6 M-18 14 L18 14 M-18 22 L8 22" stroke="#04061a" strokeWidth="3" strokeLinecap="round"/>
            <path d="M14 -22 L26 -22 L26 -10 Z" fill="#04061a"/>
          </g>
        );
      default:
        return null;
    }
  })();

  // Blush dots appear on happy expressions
  const showBlush = expression === "smile" || expression === "laugh" || expression === "love";

  return (
    <svg
      viewBox="0 0 200 200" width={size} height={size}
      className={`bns-avatar ${interactive ? "is-interactive" : ""} ${squish ? "is-squish" : ""}`}
      onClick={cycle}
      role={interactive ? "button" : undefined}
      tabIndex={interactive ? 0 : undefined}
      aria-label={interactive ? "Tap to change expression" : undefined}
      onKeyDown={interactive ? (e) => { if (e.key === 'Enter' || e.key === ' ') cycle(e); } : undefined}
    >
      <defs>
        <radialGradient id={`g-${seed}`} cx="50%" cy="40%" r="65%">
          <stop offset="0%" stopColor="rgba(255,255,255,0.35)" />
          <stop offset="100%" stopColor="rgba(0,0,0,0)" />
        </radialGradient>
      </defs>
      <rect x="0" y="0" width="200" height="200" rx="34" fill={color}/>
      <rect x="0" y="0" width="200" height="200" rx="34" fill={`url(#g-${seed})`}/>

      <g className="face">
        <circle cx="100" cy="100" r="60" fill="#fff" stroke="#04061a" strokeWidth="5"/>
        {manga ? <MangaEyes expression={expression}/> : <Eyes expression={expression}/>}
        {manga ? <MangaMouth kind={MOUTH_FOR(expression)}/> : <Mouth kind={MOUTH_FOR(expression)} color={color}/>}
        {manga && <path d="M96 116 q4 5 8 0" stroke="#04061a" strokeWidth="2.4" fill="none" strokeLinecap="round"/>}
        {showBlush && (
          manga ? <MangaBlush/> : (
            <g opacity=".7">
              <ellipse cx="62" cy="120" rx="8" ry="4" fill={color}/>
              <ellipse cx="138" cy="120" rx="8" ry="4" fill={color}/>
            </g>
          )
        )}
      </g>
      {accessory}
      <g transform="translate(170 30)" opacity=".85">
        <path d="M0 -10 L3 -3 L10 0 L3 3 L0 10 L-3 3 L-10 0 L-3 -3 Z" fill="#fff"/>
      </g>
    </svg>
  );
}

// ─── BNS Wordmark ────────────────────────────────────────────────────────
function Wordmark({ size = 88, color = "var(--c-about)" }) {
  return (
    <div className="bns-mark" style={{ ['--mark-color']: color, ['--mark-size']: `${size}px` }}>
      <span>B</span><span>N</span><span>S</span>
    </div>
  );
}

// ─── Bookmark badge ──────────────────────────────────────────────────────
function Badge({ children, tone = "paper" }) {
  return <span className={`bns-badge bns-badge--${tone}`}>{children}</span>;
}

// ─── Star rating display ─────────────────────────────────────────────────
function Pct({ value, color }) {
  return (
    <div className="bns-pct">
      <div className="bns-pct-bar"><div style={{ width: `${value}%`, background: color }}/></div>
      <span className="bns-pct-val">{value}<small>/100</small></span>
    </div>
  );
}

// ─── Component-scoped CSS injected once ──────────────────────────────────
const _bnsStyles = `
  .bns-portrait{
    border-radius: 50%;
    object-fit: cover;
    display: block;
    border: 3px solid color-mix(in srgb, var(--c, var(--c-about)) 55%, transparent);
    box-shadow: 6px 8px 0 rgba(4,6,26,.5), 0 0 50px color-mix(in srgb, var(--c, var(--c-about)) 35%, transparent);
    background: var(--ink-2);
  }
  body.theme-manga .bns-portrait{
    border: 3px solid #0a0908;
    border-radius: 0;
    box-shadow: 8px 8px 0 #0a0908;
    filter: grayscale(100%) contrast(1.05);
  }
  .bns-mark{
    display:inline-flex; gap:.04em; line-height:.85; font-family:var(--display);
    font-size:var(--mark-size); color:var(--mark-color);
    text-shadow: 4px 4px 0 #000;
  }
  .bns-mark span{ display:inline-block; }
  .bns-mark span:nth-child(2){ transform: translateY(.08em) rotate(-3deg); }
  .bns-mark span:nth-child(3){ transform: translateY(-.04em) rotate(3deg); }

  .bns-badge{
    display:inline-flex; align-items:center; gap:5px; white-space:nowrap;
    font: 600 11px/1 var(--body);
    padding:5px 9px; border-radius:99px; letter-spacing:.06em; text-transform:uppercase;
    background:rgba(244,241,232,.12); color:var(--paper);
    border:1px solid var(--line);
  }
  .bns-badge--accent{ background:var(--paper); color:#04061a; border-color:transparent; }
  .bns-badge--dot::before{ content:""; display:inline-block; width:6px;height:6px;border-radius:50%; background:#22c55e; margin-right:6px; transform:translateY(-1px); animation:pulse 1.6s ease-in-out infinite; }
  @keyframes pulse { 50%{ opacity:.4; transform:scale(1.4) translateY(-1px); } }

  .bns-pct{ display:flex; align-items:center; gap:12px; }
  .bns-pct-bar{ flex:1; height:8px; border-radius:99px; background:rgba(244,241,232,.1); overflow:hidden; }
  .bns-pct-bar > div{ height:100%; border-radius:99px; transition: width .9s cubic-bezier(.2,.9,.3,1); }
  .bns-pct-val{ font-family:var(--mono); font-size:12px; color:var(--paper-dim); min-width:64px; text-align:right; }
  .bns-pct-val small{ opacity:.5; }

  .bns-avatar{ display:block; }
  .bns-avatar.is-interactive{ cursor:pointer; user-select:none; -webkit-tap-highlight-color: transparent; }
  .bns-avatar .face{ transform-origin: 100px 110px; transition: transform .3s cubic-bezier(.3,1.6,.3,1); }
  .bns-avatar.is-interactive:hover .face{ transform: rotate(-3deg); }
  .bns-avatar.is-squish .face{ animation: squish .35s ease both; }
  @keyframes squish {
    0%   { transform: scale(1) translateY(0) rotate(0); }
    40%  { transform: scale(1.18, .82) translateY(6px) rotate(-2deg); }
    70%  { transform: scale(.92, 1.1) translateY(-4px) rotate(3deg); }
    100% { transform: scale(1) translateY(0) rotate(0); }
  }
  /* accessory wiggles on hover */
  .bns-avatar.is-interactive:hover .acc-wave{ animation: wave 0.6s ease-in-out infinite; transform-origin: 155px 65px; }
  @keyframes wave { 0%,100%{ transform: translate(155px,50px) rotate(15deg); } 50%{ transform: translate(155px,50px) rotate(-25deg); } }
  .bns-avatar.is-interactive:hover .acc-rocket{ animation: bob 1.2s ease-in-out infinite; }
  @keyframes bob { 0%,100%{ transform: translate(100px,30px); } 50%{ transform: translate(100px,22px) rotate(-4deg); } }
  .bns-avatar.is-interactive:hover .acc-gear{ animation: spin 2.4s linear infinite; transform-origin: 160px 40px; }
  @keyframes spin { to{ transform: translate(160px,40px) rotate(380deg); } }
`;
(function injectStyles(){
  if (!document.getElementById('bns-shared-styles')) {
    const s = document.createElement('style');
    s.id = 'bns-shared-styles';
    s.textContent = _bnsStyles;
    document.head.appendChild(s);
  }
})();

// Portrait — shows a real headshot when photo mode is on (and the file loads),
// otherwise falls back to the cartoon Avatar. Photo mode is toggled in Tweaks
// and broadcast via a 'bns:headshotchange' event (mirrors the theme pattern).
const HEADSHOT_SRC = "headshot.jpg";

function bnsLoadHeadshot() {
  try { return localStorage.getItem('bns-headshot') === '1'; } catch { return false; }
}

function Portrait({ seed, color, size = 220, interactive = false, defaultExpression = "open", allowPhoto = false }) {
  const [photo, setPhoto] = React.useState(bnsLoadHeadshot);
  const [failed, setFailed] = React.useState(false);

  React.useEffect(() => {
    const onChange = (e) => setPhoto(!!e.detail);
    window.addEventListener('bns:headshotchange', onChange);
    return () => window.removeEventListener('bns:headshotchange', onChange);
  }, []);

  if (allowPhoto && photo && !failed) {
    return (
      <img
        className="bns-portrait"
        src={HEADSHOT_SRC}
        alt="Batsal Nath Shrestha"
        draggable={false}
        onError={() => setFailed(true)}
        style={{ width: size, height: size }}
      />
    );
  }
  return (
    <Avatar
      seed={seed}
      color={color}
      size={size}
      interactive={interactive}
      defaultExpression={defaultExpression}
    />
  );
}

window.bnsLoadHeadshot = bnsLoadHeadshot;

Object.assign(window, { Avatar, Portrait, Wordmark, Badge, Pct });
