'use client';

import { useAuthStore } from '@/stores/auth-store';
import type { HostActionKey } from '@pac/types';

/**
 * 宿主动作跳转 URL —— 通用占位符替换。
 *
 * 模板存于 host.actionUrls[key](会话下发到 user.actionUrls),形如
 *   `/emr/MedicalAdmin/full?tenantId={brandId}&emrId={medicalRecordNumber}&organizationId={clinicId}&patientId={patientId}`
 * 把 `{xxx}` 逐个替换成 ctx[xxx](URL 编码);缺失的键替换成空串(不报错)。
 *
 * 约定占位符(驼峰,与 HOST_ACTION_META 一致):
 *   {patientId} {medicalRecordNumber} {brandId} {clinicId} {doctorId} {appointmentId} ...
 */
export function fillActionUrl(
  template: string,
  ctx: Record<string, string | null | undefined>,
): string {
  return template.replace(/\{(\w+)\}/g, (_, key: string) =>
    encodeURIComponent(ctx[key] ?? ''),
  );
}

/** 取某个宿主动作的模板(会话下发的 actionUrls);未配置返回 undefined。 */
export function actionTemplate(key: HostActionKey): string | undefined {
  return useAuthStore.getState().user?.actionUrls?.[key] || undefined;
}

/**
 * 便捷:填好某动作的 URL。未配置模板 → 返回 undefined(调用方据此决定不渲染按钮)。
 */
export function resolveActionUrl(
  key: HostActionKey,
  ctx: Record<string, string | null | undefined>,
): string | undefined {
  const tpl = actionTemplate(key);
  return tpl ? fillActionUrl(tpl, ctx) : undefined;
}

/**
 * ⭐ 宿主槽位跳转一律**新开标签页** —— 所有槽位统一,别再各写各的 target。
 *
 * 2026-07-29 业务约定:客户档案 / 病历 / 预约等宿主页面「打开新页,不在当前页面做跳转」。
 * 原来用的是 `_top`:PAC 嵌在宿主 iframe 里时会把**整个宿主页顶掉**,客服看完档案回不到
 * 刚才的工单(召回上下文、已填的通话结果全丢)。新标签页则两边都在。
 *
 * 用 a 标签的地方用 HOST_LINK_PROPS,用 JS 派发的地方用 openHostUrl(),两条路同一口径。
 * `noopener`:新页拿不到 window.opener,防宿主页被反向导航(tabnabbing)。
 *
 * ⚠️ **a 标签必须同时挂 onClick={openHostUrl}**(见各调用点):宿主用 sandbox iframe 嵌 PAC 时,
 * 声明式的 target="_blank" 会被浏览器直接拦掉、且拦不住也拦不到 —— 只有走 JS 才有兜底余地。
 * href 保留是为了右键"复制链接地址"仍可用。
 */
export const HOST_LINK_PROPS = { target: '_blank', rel: 'noopener noreferrer' } as const;

/**
 * ⭐ 打开宿主页 —— 新标签页优先,**被 sandbox 拦掉时逐级兜底**。
 *
 * 2026-07-30 线上(嵌宿主)实证,控制台报:
 *   `Blocked opening '<url>' in a new window because the request was made in a sandboxed
 *    frame whose 'allow-popups' permission is not set.`
 * 宿主的 `<iframe sandbox>` 没给 `allow-popups` → window.open 直接被拦,**点了没反应**。
 * 上一版只写了一行 window.open,连"被拦了"都不知道。
 *
 * 三级兜底(能开新页就开新页,开不了也要让人到得了那个页面):
 *   ① 新标签页(业务要的形态)
 *   ② 顶层跳转(老 `_top` 行为)—— 会顶掉宿主页,但比"点了没反应"好;
 *      sandbox 若也没给 allow-top-navigation,赋值会抛 SecurityError,继续降级
 *   ③ 本 frame 内跳转 —— 一定成立,PAC 自己被替换掉(客服可用宿主的返回回来)
 *
 * ⚠️ 检测被拦**不能靠 window.open 的返回值配 noopener**:features 里带 noopener 时
 * 规范规定**成功也返回 null**,那样永远分不清是被拦还是开成功了。
 * 所以改成先开同源 about:blank、拿到句柄后手工断 opener 再导航 —— 效果同 noopener,但可检测。
 *
 * 根治仍在宿主侧:请对接方给 iframe 的 sandbox 加上 `allow-popups`(想保留新页则再加
 * `allow-popups-to-escape-sandbox`,否则新页会继承沙箱限制)。这里只是让 PAC 别装死。
 */
export function openHostUrl(url: string): void {
  const opened = window.open('', '_blank');
  if (opened) {
    try {
      opened.opener = null; // about:blank 同源,能设;等价于 noopener,防 tabnabbing
    } catch {
      /* 设不上就算了 —— 比开不出页面轻得多 */
    }
    opened.location.replace(url);
    return;
  }

  // 被拦了 —— 只能退回"跳转"。⚠️ 这里**不弹 toast**:下一行就导航走了,提示根本来不及被看见
  // (试过,页面卸载时 toast 刚挂上就消失)。要留痕就留给控制台,那是给对接方看的。
  console.warn(
    `[pac] 新标签页被宿主 iframe 的 sandbox 拦下(缺 allow-popups),降级为跳转:${url}\n` +
      '根治:请宿主给 <iframe sandbox> 加 allow-popups(想让新页不继承沙箱再加 allow-popups-to-escape-sandbox)',
  );
  try {
    // 顶层跳转(老 `_top` 行为):会顶掉宿主页,但比"点了没反应"好。
    // sandbox 没给 allow-top-navigation 时赋值会抛 SecurityError → 落到本 frame 内跳。
    if (window.top && window.top !== window.self) {
      window.top.location.href = url;
      return;
    }
  } catch {
    /* 继续降级 */
  }
  window.location.href = url;
}
