/**
 * 挂号实时提醒工具（医生端）
 *
 * xk-api 在挂号支付成功后通过 Go WebSocket 推 message_type=15 的系统消息，
 * useWebSocket 收到后调用 handleRegisterNotifyMessage 统一处理：
 * - 桌面端（Electron）且窗口未聚焦：弹系统级消息通知 + 任务栏闪烁 + 播放提示音，
 *   点击通知唤起窗口并跳转挂号订单页（走 basic.vue 已接线的 desktop.onNavigate）
 * - 页面端（浏览器 / 桌面端窗口聚焦中）：弹站内 antd notification + 播放提示音
 */
import { notification } from 'ant-design-vue';

import { router } from '#/router';
import { desktop, isElectron } from '#/util/desktop';

// 与 util/tool.ts 的 sys.mp3 同方式引入：经 Vite 处理，生产构建自动带部署 base 路径
import RegisterNotifyMp3 from '/public/audio/register-notify.mp3';

/** 与后端 App\Enum\chat\MessageTypeEnum::REGISTER_NOTIFY 对齐 */
export const REGISTER_NOTIFY_MESSAGE_TYPE = 15;

/** 挂号订单列表菜单路由（xk_menu path），通知点击后的统一落点 */
const REGISTER_ORDER_ROUTE = '/business/register/list';

/** 去重窗口：同一挂号单 10 秒内只提醒一次（防多标签页/重复投递叠加提示音） */
const DEDUPE_WINDOW_MS = 10_000;
const DEDUPE_KEY_PREFIX = 'xk-register-notify:';

/** 后端 RegisterDoctorWsNotifyJob 拼装的通知载荷（message_content JSON） */
interface RegisterNotifyPayload {
  body: string;
  order_no?: string;
  register_id: number;
  scene?: string;
  title: string;
}

/** 提示音单例：复用同一个 Audio 对象，避免每次通知都新建请求 */
let audioInstance: HTMLAudioElement | null = null;

/**
 * 播放通知提示音
 * play() 返回的 Promise 在浏览器自动播放策略拒绝时会 reject（用户从未与页面交互过），
 * 这里静默吞掉：医生登录后必然点击过页面，正常场景不会被拦
 */
function playNotifySound() {
  try {
    if (!audioInstance) {
      audioInstance = new Audio(RegisterNotifyMp3);
    }
    audioInstance.currentTime = 0;
    audioInstance.play().catch(() => {});
  } catch {
    // Audio 不可用（极老浏览器）时跳过声音，不影响通知展示
  }
}

/**
 * 解析 WS 消息里的通知载荷
 * message_content 是后端 json_encode 的字符串；解析失败返回 null 直接忽略该条
 */
function parsePayload(raw: unknown): null | RegisterNotifyPayload {
  try {
    const data = typeof raw === 'string' ? JSON.parse(raw) : raw;
    if (!data || typeof data !== 'object') return null;
    const record = data as Record<string, unknown>;
    const registerId = Number(record.register_id);
    if (!Number.isFinite(registerId) || registerId <= 0) return null;
    return {
      register_id: registerId,
      title: String(record.title || '新的挂号订单'),
      body: String(record.body || ''),
      order_no: String(record.order_no || ''),
      scene: String(record.scene || ''),
    };
  } catch {
    return null;
  }
}

/**
 * 同一挂号单去重：localStorage 记录上次提醒时间戳
 * 医生开多个标签/窗口时每个连接都会收到推送（Go 服务发给用户所有在线设备），
 * 不去重会同时响多次提示音；localStorage 跨标签共享，天然适合做这个锁
 */
function shouldNotify(registerId: number): boolean {
  const key = DEDUPE_KEY_PREFIX + registerId;
  try {
    const last = Number(localStorage.getItem(key) || 0);
    const now = Date.now();
    if (now - last < DEDUPE_WINDOW_MS) return false;
    localStorage.setItem(key, String(now));
    return true;
  } catch {
    // localStorage 不可用（隐私模式）时放行，宁可重复提醒不可漏提醒
    return true;
  }
}

/**
 * 弹站内通知卡片
 * key 用挂号单 ID 保证同单只显示一张卡片；点击卡片跳转挂号订单页并收起。
 * 页面在后台标签时不自动关闭（duration=0），医生切回来仍能看到提醒
 */
function showInPageNotification(payload: RegisterNotifyPayload) {
  const key = `register-notify-${payload.register_id}`;
  notification.open({
    key,
    message: payload.title,
    description: payload.body || payload.order_no,
    duration: document.hidden ? 0 : 8,
    onClick: () => {
      router.push(REGISTER_ORDER_ROUTE).catch(() => {});
      notification.close(key);
    },
  });
}

/**
 * 挂号提醒统一入口（useWebSocket 收到 message_type=15 时调用）
 * 流程：解析载荷 → 跨标签去重 → 播放提示音 → 按运行环境选择通知形态
 */
export function handleRegisterNotifyMessage(data: {
  message_content?: unknown;
}) {
  const payload = parsePayload(data?.message_content);
  if (!payload || !shouldNotify(payload.register_id)) return;

  playNotifySound();

  // 桌面端窗口未聚焦：系统通知 + 任务栏闪烁（页内卡片此时用户看不到），
  // 点击通知由主进程唤起窗口并回传路由（basic.vue 的 desktop.onNavigate 执行跳转）
  if (isElectron() && !document.hasFocus()) {
    desktop.notify({
      title: payload.title,
      body: payload.body || payload.order_no || '',
      route: REGISTER_ORDER_ROUTE,
    });
    desktop.flashFrame();
    return;
  }

  // 浏览器端 / 桌面端窗口聚焦中：站内提示
  showInPageNotification(payload);
}
