'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 自己被替换掉(客服可用宿主的返回回来)
 *
 * ⚠️ **必须一步 open 带上 URL,不能"先开 about:blank 再导航"**(2026-07-30 踩过):
 * 那一版是为了"开完手工断 opener 以等效 noopener,同时保留可检测性"。宿主加上 allow-popups 之后
 * 立刻炸了两条:
 *   `Unsafe attempt to initiate navigation for frame with URL 'about:blank' … The frame
 *    attempting navigation is sandboxed and is trying to navigate a popup, but is not the
 *    popup's opener and is not set to propagate sandboxing to popups.`
 *   `Uncaught SecurityError: Failed to execute 'replace' on 'Location': The current window
 *    does not have permission to navigate the target frame to '<host url>'.`
 * 因果很直白:**opener 一断,我们就不再是那个弹窗的 opener**,而沙箱化的 frame 只有作为 opener
 * 才有权导航它 —— 于是第二步 `location.replace` 被拒,新标签页停在空白页。
 * 顺序反过来(先导航后断 opener)也不行:导航到跨源之后再设 opener 会抛。
 *
 * 所以结论是:**一步到位 `window.open(url, '_blank')`**。
 *   · 可检测性还在 —— 被弹窗拦时返回 null(features 里**不写** noopener;写了成功也返回 null,
 *     那才是分不清被拦和成功的那种写法)
 *   · opener 只做**尽力而为**的切断(跨源会抛,catch 掉即可)。这里能接受:目标 URL 是宿主管理页
 *     配好的自家地址,不是用户输入的任意站点,tabnabbing 面本来就很窄。
 *
 * 根治仍在宿主侧:`sandbox` 要有 `allow-popups`;**并且建议加 `allow-popups-to-escape-sandbox`**
 * —— 只给 allow-popups 时新标签页会继承沙箱,宿主自己的页面在里面可能功能不全。
 */
export function openHostUrl(url: string): void {
  const opened = window.open(url, '_blank');
  if (opened) {
    try {
      opened.opener = null; // 尽力而为:同源能断,跨源会抛(见上方注释,不影响已经开出来的页)
    } catch {
      /* 断不掉就算了 —— 目标是宿主自家配置的地址 */
    }
    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;
}
