/**
 * src/lib/nlu/pipeline.ts
 *
 * 四阶段语音处理链路：
 *   阶段 0: 停止词检测
 *   阶段 1: UI 可见元素语音点击匹配（1a: waiting_confirmation / 1b: Artifact 按钮）
 *   阶段 1.5: waiting_slot + 数值输入
 *   阶段 2: BERT NLU（调 intelligent_cabin）
 *
 * 规则：报错直接抛出，不降级，不 Mock 兜底。
 */

import { AFFIRM_WORDS, CANCEL_WORDS, DENY_WORDS } from "./voice-aliases.gen";
import { chatWithNlu, fillSlotsWithNlu } from "./client";
import {
  adaptChatResponse,
  type NluResult,
  type PipelineEvent,
  type PipelineResult,
  type PipelineSession,
  type SlotFillEvent,
  type StopActionEvent,
  type VisibleAction,
  type VoiceClickEvent,
} from "./types";

// ── 文本标准化 ───────────────────────────────────────────────────────────────

function normalizeVoice(text: string): string {
  return text
    .trim()
    .toLowerCase()
    .replace(/[，。！？、\s]+/g, "")
    .replace(/^(好的?|嗯+|那就|那么|好吧)/, "");
}

function includesNormalized(haystack: string, needle: string): boolean {
  return haystack.includes(normalizeVoice(needle));
}

// ── 阶段 0：停止词检测 ────────────────────────────────────────────────────────

function detectStopWord(text: string): StopActionEvent | null {
  const norm = normalizeVoice(text);
  if (CANCEL_WORDS.some((w) => includesNormalized(norm, w))) {
    return { type: "stop_action", sourceText: text };
  }
  return null;
}

// ── 阶段 1a：waiting_confirmation 下的 affirm/deny ────────────────────────────

function detectConfirmationResponse(
  text: string,
  status: string,
): VoiceClickEvent | null {
  if (status !== "waiting_confirmation") return null;
  const norm = normalizeVoice(text);

  if (AFFIRM_WORDS.some((w) => includesNormalized(norm, w))) {
    return { type: "voice_click_event", actionId: "artifact.confirm", sourceText: text };
  }
  if (DENY_WORDS.some((w) => includesNormalized(norm, w))) {
    return { type: "voice_click_event", actionId: "artifact.cancel", sourceText: text };
  }
  return null;
}

// ── 阶段 1b：当前可见 Artifact 按钮匹配 ──────────────────────────────────────

function detectVoiceClick(
  text: string,
  visibleActions: VisibleAction[],
): VoiceClickEvent | null {
  const norm = normalizeVoice(text);
  for (const action of visibleActions) {
    const matched = action.matchPhrases.some((phrase) =>
      includesNormalized(norm, phrase),
    );
    if (matched) {
      return {
        type: "voice_click_event",
        actionId: action.actionId,
        artifactId: action.artifactId,
        sourceText: text,
      };
    }
  }
  return null;
}

// ── 阶段 1.5：waiting_slot + 数值输入 ────────────────────────────────────────

const NUMERIC_PATTERN = /[-+]?\d+(?:\.\d+)?/;

function detectSlotFill(text: string, status: string): SlotFillEvent | null {
  if (status !== "waiting_slot") return null;
  const match = text.match(NUMERIC_PATTERN);
  if (!match) return null;
  return { type: "slot_fill_event", value: match[0], sourceText: text };
}

// ── 主流程 ───────────────────────────────────────────────────────────────────

/**
 * 处理单次用户输入，返回拦截事件或 NLU 识别结果。
 *
 * @param text        - ASR 转录文本或直接文字输入
 * @param session     - 当前画布会话状态（status + visibleActions）
 * @param userId      - 用户 ID，用于 NLU 服务 session 跟踪
 *
 * @throws 当 intelligent_cabin 服务不可达或返回错误时直接抛出，不降级
 */
export async function processVoiceInput(
  text: string,
  session: PipelineSession,
  userId: string = "default",
): Promise<PipelineResult> {

  // ── 阶段 0：停止词 ──────────────────────────────────────────────────────────
  const stopEvent = detectStopWord(text);
  if (stopEvent) return stopEvent;

  // ── 阶段 1a：waiting_confirmation affirm/deny ───────────────────────────────
  const confirmEvent = detectConfirmationResponse(text, session.status);
  if (confirmEvent) return confirmEvent;

  // ── 阶段 1b：可见 Artifact 按钮 ────────────────────────────────────────────
  const voiceClickEvent = detectVoiceClick(text, session.visibleActions);
  if (voiceClickEvent) return voiceClickEvent;

  // ── 阶段 1.5：waiting_slot 数值填写 ────────────────────────────────────────
  const slotFillEvent = detectSlotFill(text, session.status);
  if (slotFillEvent) {
    // 通知 intelligent_cabin 填槽，更新服务端 session 状态
    const raw = await fillSlotsWithNlu({
      session_id: session.sessionId,
      user_id: userId,
      input_text: text,
    });
    // 返回 slot_fill_event 让 Canvas 状态机处理，NLU 结果附在事件上
    return {
      ...slotFillEvent,
      // 附上后端确认的槽位信息，便于 Canvas 更新 Artifact
      // @ts-expect-error 扩展字段，Canvas 侧 reducer 可选消费
      nluResult: adaptChatResponse(raw),
    };
  }

  // ── 阶段 2：BERT NLU ────────────────────────────────────────────────────────
  const raw = await chatWithNlu({
    session_id: session.sessionId,
    user_id: userId,
    input_text: text,
  });

  return adaptChatResponse(raw);
}

// ── 工具：从 ArtifactAction[] 构建 VisibleAction[] ───────────────────────────

/**
 * 将当前激活 Artifact 的 actions 转换成 pipeline 能消费的 VisibleAction[]。
 * 在 Artifact 变更时调用，更新 session.visibleActions。
 */
export function buildVisibleActions(
  artifactId: string,
  actions: Array<{ actionId: string; label: string; textAliases: string[]; voiceAliases?: string[] }>,
): VisibleAction[] {
  return actions.map((action) => ({
    actionId: action.actionId,
    artifactId,
    matchPhrases: [
      action.label,
      ...action.textAliases,
      ...(action.voiceAliases ?? []),
    ],
  }));
}

// ── 工具：判断 PipelineResult 类型 ──────────────────────────────────────────

export function isNluResult(result: PipelineResult): result is NluResult {
  return !("type" in result);
}

export function isPipelineEvent(result: PipelineResult): result is PipelineEvent {
  return "type" in result;
}
