'use client';

import { useEffect, useRef } from 'react';
import { env } from '@/lib/env';
import { emitPetEvent } from '@/lib/pet-events';
import { useAuthStore } from '@/stores/auth-store';
import { usePlanSyncStore } from '@/stores/plan-sync-store';

/**
 * 宠物开口说话(LLM 导演的第一个落地)— 观察周围环境,低频说一句简短的话。
 *
 * 纪律(对齐 PAC 原则 5):
 *  - 只在页面激活(visible + focus)时才可能触发;低概率 + 长冷却,绝不刷屏;
 *  - 输入极简(一段 ≤400 字的环境观察文本),输出极短(≤30 字,流式进气泡);
 *  - LLM 挂了 = 静默不说话,反射层(规则宠物)完全不受影响。
 */
const CHECK_MS = 75_000; // 检查节拍
const SPEAK_PROB = 0.12; // 每拍触发概率
const COOLDOWN_MS = 4 * 60_000; // 两次发言最小间隔

/** 环境感知:拼一段简短的观察文本(宠物"看到"什么)。 */
function sense(btn: HTMLElement | null): string {
  const parts: string[] = [];
  const h = new Date().getHours();
  parts.push(
    `现在是${h < 6 ? '深夜' : h < 9 ? '清晨' : h < 12 ? '上午' : h < 14 ? '中午' : h < 18 ? '下午' : '晚上'}`,
  );
  const cur = usePlanSyncStore.getState().current;
  if (cur?.patientName) parts.push(`客服正在跟进患者「${cur.patientName}」的召回工作台`);
  const r = btn?.getBoundingClientRect();
  if (r) {
    const onFloor = r.bottom > window.innerHeight - 70;
    if (onFloor) {
      parts.push('我正站在屏幕底边');
    } else {
      // 身下是什么卡片:取其最近容器里的标题文字
      const below = document.elementFromPoint(
        Math.min(Math.max(r.left + r.width / 2, 1), window.innerWidth - 1),
        Math.min(r.bottom + 6, window.innerHeight - 1),
      );
      const card = below?.closest('section, aside, figure, header, [data-pet-perch]');
      const title = card
        ?.querySelector('h1,h2,h3,figcaption,[class*="font-semibold"]')
        ?.textContent?.trim()
        .slice(0, 24);
      parts.push(title ? `我正蹲在「${title}」卡片的边框上` : '我蹲在一个卡片边框上');
    }
  }
  return parts.join(';');
}

export function usePetVoice(btnRef: React.RefObject<HTMLElement | null>, canSpeak: () => boolean) {
  const lastAt = useRef(0);
  const speaking = useRef(false);

  useEffect(() => {
    const speak = async () => {
      if (speaking.current) return;
      speaking.current = true;
      lastAt.current = Date.now();
      // 一发起就进"思考"(还没收到第一个词)→ 思考脸 + 头顶冒点
      emitPetEvent({ type: 'pet_voice', phase: 'think' });
      let talking = false;
      try {
        const token = useAuthStore.getState().accessToken;
        const res = await fetch(new URL('/pac/v1/assistant/pet-say', env.apiBaseUrl), {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            Accept: 'text/event-stream',
            ...(token ? { Authorization: `Bearer ${token}` } : {}),
          },
          body: JSON.stringify({ observation: sense(btnRef.current) }),
        });
        if (!res.ok || !res.body) return; // 静默降级(finally 会收尾)
        let acc = '';
        let buffer = '';
        const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
        while (true) {
          const { value, done } = await reader.read();
          if (done) break;
          buffer += value;
          let sep: number;
          while ((sep = buffer.indexOf('\n\n')) !== -1) {
            const frame = buffer.slice(0, sep);
            buffer = buffer.slice(sep + 2);
            const line = frame.split('\n').find((l) => l.startsWith('data:'));
            if (!line) continue;
            try {
              const evt = JSON.parse(line.slice('data:'.length).trim()) as { type?: string; text?: string };
              if (evt.type === 'text' && evt.text) {
                if (!talking) {
                  talking = true;
                  emitPetEvent({ type: 'pet_voice', phase: 'talk' }); // 第一个词到 → 切"说话"(嘴动)
                }
                acc += evt.text;
                // 流式进气泡:每个增量重发(ttl 给长,防中途消失;说完再收短)
                emitPetEvent({ type: 'director', script: { bubble: acc, ttlMs: 15_000 } });
              }
            } catch {
              /* 半截帧忽略 */
            }
          }
        }
        if (acc) emitPetEvent({ type: 'director', script: { bubble: acc, ttlMs: 4_500 } });
      } catch {
        /* LLM 不可用 → 保持沉默,反射层照常活 */
      } finally {
        emitPetEvent({ type: 'pet_voice', phase: 'end' }); // 停止说话动作,复位基线
        speaking.current = false;
      }
    };

    const iv = setInterval(() => {
      if (document.visibilityState !== 'visible' || !document.hasFocus()) return; // 只在页面激活时
      if (Date.now() - lastAt.current < COOLDOWN_MS) return;
      if (Math.random() > SPEAK_PROB) return;
      if (!canSpeak()) return;
      void speak();
    }, CHECK_MS);
    // lab 一键触发(跳过概率/冷却,保留激活态要求)
    const h = (e: Event) => {
      if ((e as CustomEvent<{ cmd?: string }>).detail?.cmd === 'say') void speak();
    };
    window.addEventListener('pac-pet-debug', h);
    return () => {
      clearInterval(iv);
      window.removeEventListener('pac-pet-debug', h);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
}
