// cat-pet.jsx — a small pixel cat that hangs out above the taskbar.
// Sprite sheet: icons/Cat Sprite Sheet.png — 256x320, 8 cols x 10 rows of 32x32.
// Rendered at 2x (64x64 frame). Behavior is a small FSM that picks the next
// action based on what the cat is currently doing — naps follow stretches,
// grooming follows sitting, the cat occasionally bolts in a short run, etc.

const CAT_SHEET = 'icons/Cat%20Sprite%20Sheet.png';
const CAT_FRAME = 32;
const CAT_SCALE = 2;
const CAT_DISPLAY = CAT_FRAME * CAT_SCALE; // 64

// { row, frames, fps, naturalDir }. naturalDir is the direction the sprite is
// drawn facing on the sheet (1 = right, -1 = left). Side animations on this
// sheet face left, so we flip when moving right.
const CAT_ANIM = {
  sitBack:  { row: 0, frames: 4, fps: 3 },
  groom:    { row: 1, frames: 4, fps: 4 },
  sitTail:  { row: 2, frames: 4, fps: 3 },
  stretch:  { row: 3, frames: 4, fps: 4 },
  walk:     { row: 4, frames: 8, fps: 8, naturalDir: 1 },
  run:      { row: 5, frames: 8, fps: 14, naturalDir: 1 },
  sleep:    { row: 6, frames: 4, fps: 2 },
  sitFront: { row: 7, frames: 6, fps: 4 },
  pace:     { row: 9, frames: 7, fps: 6 },
};

// minMs/maxMs = action duration; speed = horizontal px/sec for moving actions.
const CAT_PLAN = {
  walk:     { minMs: 2500, maxMs: 5500, speed: 22 },
  run:      { minMs: 700,  maxMs: 1600, speed: 75 },
  sitFront: { minMs: 1800, maxMs: 4000 },
  sitBack:  { minMs: 1500, maxMs: 3500 },
  sitTail:  { minMs: 1500, maxMs: 3500 },
  groom:    { minMs: 2400, maxMs: 4800 },
  stretch:  { minMs: 1300, maxMs: 1800 },
  sleep:    { minMs: 4000, maxMs: 9000 },
  pace:     { minMs: 1500, maxMs: 3000 },
};

// Markov-ish next-state weights — cats sit a lot, walk a bit, occasionally
// bolt, groom while sitting, stretch before/after sleep.
const CAT_NEXT = {
  walk:     [['sitFront', 3], ['sitTail', 2], ['walk', 2], ['run', 1], ['sitBack', 2]],
  run:      [['walk', 3], ['sitFront', 1]],
  sitFront: [['groom', 3], ['walk', 2], ['sitTail', 1], ['sleep', 1]],
  sitBack:  [['walk', 3], ['groom', 1], ['sitTail', 1]],
  sitTail:  [['walk', 2], ['sitFront', 2], ['groom', 1]],
  groom:    [['sitFront', 3], ['stretch', 1], ['walk', 2]],
  stretch:  [['sleep', 3], ['walk', 1]],
  sleep:    [['stretch', 4], ['sitFront', 1]],
  pace:     [['sitFront', 1], ['walk', 2]],
};

const CAT_LINES = [
  "This was Eevee, the first cat in my life. RIP babygirl <3",
  "Eevee loves to hang out here",
  "I hope you're a cat person, and if not then shoo",
  "There are a lot of easter eggs hidden around the site yk",
  "Hello there stranger! Can we be friends? Feed me.",
  "I have heard rumours about there being hidden food for me around that website, I just cant seem to find it",
  "Meow meow generic cat stuff meow meow, now do you believe I am a real cat?",
  "Stop touching me human!",
  "Ouch",
  "B*tch dont touch me",
  "Okay where is my knife?",
  "Okay didnt you come here to buy something?",
  "Go buy stuff so I can make money",
  "Need to feed the cat, please buy shit.",
  "hey yo mr.white lets cook",
];

function catPickWeighted(pairs) {
  const total = pairs.reduce((s, p) => s + p[1], 0);
  let r = Math.random() * total;
  for (const [name, w] of pairs) {
    if ((r -= w) <= 0) return name;
  }
  return pairs[0][0];
}
const catRand = (a, b) => a + Math.random() * (b - a);

function CatPet() {
  const wrapRef = React.useRef(null);
  const containerRef = React.useRef(null);
  const stateRef = React.useRef({
    x: 80,
    dir: Math.random() < 0.5 ? -1 : 1,
    action: 'walk',
    frame: 0,
    actionStart: 0,
    actionDur: catRand(CAT_PLAN.walk.minMs, CAT_PLAN.walk.maxMs),
    lastFrameAt: 0,
  });
  const [, forceTick] = React.useReducer((n) => n + 1, 0);
  const [bubble, setBubble] = React.useState(null);
  const lastLineRef = React.useRef(-1);
  const bubbleTimerRef = React.useRef(null);

  React.useEffect(() => {
    const el = wrapRef.current;
    if (!el) return;
    let p = el.parentElement;
    while (p && getComputedStyle(p).position === 'static') p = p.parentElement;
    containerRef.current = p || document.body;
    stateRef.current.actionStart = performance.now();
  }, []);

  React.useEffect(() => {
    let raf;
    let last = performance.now();
    const loop = (t) => {
      const st = stateRef.current;
      const dt = Math.min(60, t - last);
      last = t;

      const anim = CAT_ANIM[st.action];
      const plan = CAT_PLAN[st.action] || { minMs: 1500, maxMs: 3000 };

      const frameMs = 1000 / anim.fps;
      if (t - st.lastFrameAt >= frameMs) {
        st.frame = (st.frame + 1) % anim.frames;
        st.lastFrameAt = t;
      }

      if (plan.speed) {
        const w = (containerRef.current?.clientWidth || window.innerWidth);
        const minX = 4;
        const maxX = Math.max(minX + 1, w - CAT_DISPLAY - 4);
        st.x += st.dir * plan.speed * (dt / 1000);
        if (st.x <= minX) { st.x = minX; st.dir = 1; }
        else if (st.x >= maxX) { st.x = maxX; st.dir = -1; }
      }

      if (t - st.actionStart >= st.actionDur) {
        const next = catPickWeighted(CAT_NEXT[st.action] || [['sitFront', 1]]);
        if ((next === 'walk' || next === 'run') && Math.random() < 0.45) {
          st.dir = -st.dir;
        }
        st.action = next;
        st.frame = 0;
        st.actionStart = t;
        const np = CAT_PLAN[next] || { minMs: 1500, maxMs: 3000 };
        st.actionDur = catRand(np.minMs, np.maxMs);
      }

      forceTick();
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(raf);
  }, []);

  const onPet = (e) => {
    if (e && e.stopPropagation) e.stopPropagation();
    // Pick a fresh line that isn't the last one shown.
    let idx = Math.floor(Math.random() * CAT_LINES.length);
    if (CAT_LINES.length > 1 && idx === lastLineRef.current) {
      idx = (idx + 1) % CAT_LINES.length;
    }
    lastLineRef.current = idx;
    setBubble(CAT_LINES[idx]);
    if (bubbleTimerRef.current) clearTimeout(bubbleTimerRef.current);
    bubbleTimerRef.current = setTimeout(() => setBubble(null), 4200);
    // Park the cat for a moment so the bubble has time to read.
    const st = stateRef.current;
    st.action = 'sitFront';
    st.frame = 0;
    st.actionStart = performance.now();
    st.actionDur = 4200;
  };

  React.useEffect(() => () => {
    if (bubbleTimerRef.current) clearTimeout(bubbleTimerRef.current);
  }, []);

  const st = stateRef.current;
  const anim = CAT_ANIM[st.action];
  const bgX = -st.frame * CAT_FRAME * CAT_SCALE;
  const bgY = -anim.row * CAT_FRAME * CAT_SCALE;
  const nat = anim.naturalDir || 1;
  const flip = (nat === st.dir) ? 1 : -1;

  // Keep bubble inside the desktop area horizontally — clamp its left offset
  // relative to the sprite so it doesn't get clipped near the edges.
  const containerW = containerRef.current?.clientWidth || window.innerWidth;
  const bubbleW = 180;
  const bubbleLeft = Math.min(
    Math.max(-((bubbleW - CAT_DISPLAY) / 2), 6 - st.x),
    (containerW - bubbleW - 6) - st.x
  );

  return (
    <div
      ref={wrapRef}
      style={{
        position: 'absolute',
        left: 0,
        bottom: 2,
        width: CAT_DISPLAY,
        height: CAT_DISPLAY,
        transform: `translate3d(${st.x}px, 0, 0)`,
        zIndex: 50,
        pointerEvents: 'none',
      }}
    >
      {bubble && (
        <div
          onClick={(e) => { e.stopPropagation(); setBubble(null); }}
          style={{
            position: 'absolute',
            left: bubbleLeft,
            bottom: CAT_DISPLAY - 36,
            width: bubbleW,
            background: '#ffffe1',
            color: '#000',
            border: '1px solid #000',
            boxShadow: '2px 2px 0 #000',
            padding: '6px 8px',
            fontSize: 11,
            lineHeight: 1.35,
            fontFamily: 'inherit',
            pointerEvents: 'auto',
            cursor: 'pointer',
            imageRendering: 'auto',
            zIndex: 2,
          }}
        >
          <div style={{ wordWrap: 'break-word' }}>{bubble}</div>
          {/* Speech-bubble tail pointing down at the cat */}
          <div style={{
            position: 'absolute',
            left: Math.max(8, Math.min(bubbleW - 16, -bubbleLeft + CAT_DISPLAY / 2 - 4)),
            bottom: -6,
            width: 0, height: 0,
            borderLeft: '5px solid transparent',
            borderRight: '5px solid transparent',
            borderTop: '6px solid #000',
          }} />
          <div style={{
            position: 'absolute',
            left: Math.max(9, Math.min(bubbleW - 15, -bubbleLeft + CAT_DISPLAY / 2 - 3)),
            bottom: -4,
            width: 0, height: 0,
            borderLeft: '4px solid transparent',
            borderRight: '4px solid transparent',
            borderTop: '5px solid #ffffe1',
          }} />
        </div>
      )}
      <div
        onClick={onPet}
        title="meow"
        aria-label="cat"
        style={{
          width: CAT_DISPLAY,
          height: CAT_DISPLAY,
          transform: `scaleX(${flip})`,
          transformOrigin: 'center bottom',
          backgroundImage: `url("${CAT_SHEET}")`,
          backgroundRepeat: 'no-repeat',
          backgroundSize: `${256 * CAT_SCALE}px ${320 * CAT_SCALE}px`,
          backgroundPosition: `${bgX}px ${bgY}px`,
          imageRendering: 'pixelated',
          pointerEvents: 'auto',
          cursor: 'pointer',
          filter: 'drop-shadow(0 1px 0 rgba(0,0,0,0.4))',
        }}
      />
    </div>
  );
}
