/**
 * StatusBarIframe 组件
 *
 * 负责渲染消息中嵌入的 HTML 内容（包含 <script> 的完整 HTML、html-code-block、或 YAML 状态栏）。
 * 内部使用 sandbox iframe + shim 脚本，与父页面通过 postMessage 桥接。
 *
 * 安全说明：
 * - sandbox 属性包含 allow-same-origin：允许 iframe 访问父页面 window（ST 兼容必需）。
 *   卡片脚本（如 Clannad）通过 window.parent 存取全局状态和 ST API。
 *   原版 ST 中脚本直接运行在主页面上下文，不存在 iframe 隔离。
 * - postMessage 使用 '*' 作为 targetOrigin（兼容 srcdoc iframe）
 * - 父页面通过 event.source === iframe.contentWindow 校验消息来源
 *
 * 对应文档第 5 节（HTML 状态栏渲染器）与重构文档 8.4 节。
 */

import { useEffect, useRef, useState } from 'react'
import { load as parseYaml } from 'js-yaml'

// ============= 类型定义 =============

interface StatusBarIframeProps {
  /** 当前消息原始内容（用于 shim 注入 __RAW_MESSAGE__） */
  rawMessage: string
  /** 所有消息原始内容（用于 shim 注入 __CHAT_MESSAGES__） */
  allMessages?: string[]
  /** 当前消息在对话中的索引 */
  messageIndex?: number
  /** 要渲染的 HTML 内容（来自 html-code-block 或直接 HTML）
   *  若同时提供 statusYaml，则以 HTML 为主，YAML 注入作兜底 */
  htmlContent?: string
  /** 状态栏 YAML 数据（来自 <Status_block>，不存在则为空字符串） */
  statusYaml?: string
  /** iframe 最小高度（px），默认 200 */
  minHeight?: number
  /**
   * 父页面 origin（postMessage 安全检查）。
   * 默认为 window.location.origin（运行时获取），而非硬编码 '*'。
   */
  parentOrigin?: string
  /**
   * iframe 内操作回调。
   * - type='fillInput'    → 仅填充输入框（sendToChatBox）
   * - type='playerAction' → 仅填充输入框（onPlayerAction / options-list 点击）
   * - type='triggerAction'→ 填充并发送（triggerSlash，命令格式如 /send text|/trigger）
   */
  onAction?: (type: 'fillInput' | 'playerAction' | 'triggerAction' | 'setChatMessage', payload: string, messageIndex?: number) => void
}

// ============= shim 脚本模板 =============

/**
 * 生成注入到 iframe 的 shim 脚本
 * 提供 ST 兼容 API + 与宿主页桥接
 *
 * 注意：由于 sandbox 去掉了 allow-same-origin，iframe 内脚本无法访问父页面 DOM / storage，
 * 与父页通信必须通过 postMessage。
 */
function buildShimScript(
  rawMessage: string,
  allMessages: string[],
  currentId: number
): string {
  // Escape </script (the HTML-spec pattern that closes a script element) to prevent
  // the inline <script> tag from being prematurely terminated by message content.
  // Per spec, </script is closed by </script followed by whitespace, '/' or '>'.
  // Replacing '</script' with '<\/script' is safe: JS treats \/ as / inside strings.
  const escapeForScript = (s: string) => s.replace(/<\/script/gi, '<\\/script')
  const rawJson = escapeForScript(JSON.stringify(rawMessage))
  const allJson = escapeForScript(JSON.stringify(allMessages))

  return `
(function() {
  var __RAW_MESSAGE__ = ${rawJson};
  var __CHAT_MESSAGES__ = ${allJson};
  var __CURRENT_ID__ = ${currentId};

  // ST 兼容 API（同时注册到 iframe window 和 parent window，与原版 ST 行为一致）
  window.getCurrentMessageId = function() { return __CURRENT_ID__; };
  window.getChatMessages = function(id) {
    var idx = (id !== undefined && id !== null) ? id : __CURRENT_ID__;
    return [{ message: __CHAT_MESSAGES__[idx] !== undefined ? __CHAT_MESSAGES__[idx] : __RAW_MESSAGE__ }];
  };

  // 沙箱内禁用模态对话框（sandbox 不允许 allow-modals，直接覆盖避免报错）
  // alert → 控制台输出；confirm → 始终返回 true；prompt → 返回空字符串
  window.alert   = function(msg)         { console.log('[sandbox alert]', msg); };
  window.confirm = function(msg)         { console.log('[sandbox confirm]', msg); return true; };
  window.prompt  = function(msg, def)    { console.log('[sandbox prompt]', msg); return (def !== undefined ? String(def) : ''); };

  // localStorage shim（兜底）：若 localStorage 不可用则用内存 Map 模拟
  (function() {
    var _store = {};
    var _ls = {
      getItem: function(k) { return Object.prototype.hasOwnProperty.call(_store, k) ? _store[k] : null; },
      setItem: function(k, v) { _store[k] = String(v); },
      removeItem: function(k) { delete _store[k]; },
      clear: function() { _store = {}; },
      key: function(i) { return Object.keys(_store)[i] || null; },
      get length() { return Object.keys(_store).length; }
    };
    try { window.localStorage; } catch(e) { Object.defineProperty(window, 'localStorage', { value: _ls, writable: false }); return; }
    try { window.localStorage.getItem('__probe__'); } catch(e) {
      Object.defineProperty(window, 'localStorage', { value: _ls, writable: false });
    }
  })();

  // 向父页面发送消息（allow-same-origin 下 origin 继承父页面，但 '*' 仍兼容所有场景）
  function sendToParent(type, data) {
    window.parent.postMessage({ type: type, data: data }, '*');
  }

  // 上报内容尺寸（父页面据此调整 iframe 大小）
  function reportSize() {
    var body = document.body;
    var html = document.documentElement;
    if (!body || !html) return;
    // body 是 inline-block，其 offsetWidth/scrollWidth 反映真实内容宽度
    // html 的 scrollHeight 反映完整文档高度
    var width = Math.max(body.offsetWidth, body.scrollWidth);
    var height = Math.max(html.scrollHeight, html.offsetHeight, body.scrollHeight, body.offsetHeight);
    sendToParent('resize', { width: width, height: height });
  }

  // ST 兼容：triggerSlash → 发送到父页面并触发发送（例：/send text|/trigger）
  window.triggerSlash = function(command) {
    sendToParent('triggerAction', { command: command });
  };

  // ST 兼容：setChatMessage(content, msgId) → 就地替换指定消息内容（不发送 AI 请求）
  // 常见于前端卡路由按钮，用于切换页面内容（如 Clannad 路由）
  window.setChatMessage = function(content, msgId) {
    sendToParent('setChatMessage', { content: content, messageId: msgId !== undefined ? msgId : __CURRENT_ID__ });
  };

  // ST 兼容：sendToChatBox → 只填充输入框，不发送
  window.sendToChatBox = function(text) {
    sendToParent('fillInput', { text: text });
  };

  // 用户操作触发（供状态栏按钮调用）
  window.onPlayerAction = function(action) {
    sendToParent('playerAction', { action: action });
  };

  // ---- ST 兼容：将 API 同步到 parent window ----
  // 原版 ST 中卡片脚本运行在主页面上下文，API 直接挂在主 window。
  // 我们用 iframe 隔离，但许多卡片（如 Clannad）通过 window.parent 访问全局状态
  // 和 ST API，因此需要将 shim API 也注册到 parent window，确保完全兼容。
  try {
    if (window.parent && window.parent !== window) {
      window.parent.getCurrentMessageId = window.getCurrentMessageId;
      window.parent.getChatMessages = window.getChatMessages;
      window.parent.setChatMessage = window.setChatMessage;
      window.parent.triggerSlash = window.triggerSlash;
      window.parent.sendToChatBox = window.sendToChatBox;
      window.parent.onPlayerAction = window.onPlayerAction;
    }
  } catch(e) {
    // allow-same-origin 未启用时忽略跨域错误
  }

  // DOMContentLoaded 后统一执行：注入 YAML 兜底数据 + 绑定 options-list + 上报尺寸
  document.addEventListener('DOMContentLoaded', function() {
    // 注入 Status_block YAML：
    //   - 若 yaml-data-source 元素不存在，创建并追加到 <head>
    //   - 若存在但内容为空（卡片模板占位），填入 YAML 内容
    var statusMatch = __RAW_MESSAGE__.match(/<Status_block>([\\s\\S]*?)<\\/Status_block>/i);
    if (statusMatch) {
      var yamlContent = statusMatch[1].trim();
      var existing = document.getElementById('yaml-data-source');
      if (!existing) {
        var s = document.createElement('script');
        s.id = 'yaml-data-source';
        s.type = 'text/yaml';
        s.textContent = yamlContent;
        document.head && document.head.appendChild(s);
      } else if (!existing.textContent || !existing.textContent.trim()) {
        // 元素存在但为空（卡片模板占位），填入数据
        existing.textContent = yamlContent;
      }
    }

    // 注入 maintext：若元素不存在则创建；若存在但为空则填入
    var maintextMatch = __RAW_MESSAGE__.match(/<maintext>([\\s\\S]*?)<\\/maintext>/i);
    if (maintextMatch) {
      var maintextContent = maintextMatch[1].trim();
      var existingMaintext = document.getElementById('maintext');
      if (!existingMaintext) {
        var d = document.createElement('div');
        d.id = 'maintext';
        d.style.display = 'none';
        d.textContent = maintextContent;
        document.body && document.body.appendChild(d);
      } else if (!existingMaintext.textContent || existingMaintext.textContent.trim() === '加载中...') {
        existingMaintext.textContent = maintextContent;
      }
    }

    // options-list 点击兜底
    var optionsList = document.getElementById('options-list');
    if (optionsList) {
      optionsList.addEventListener('click', function(event) {
        var target = event.target;
        if (target && target.textContent) {
          sendToParent('playerAction', { action: target.textContent.trim() });
        }
      }, true);
    }

    // 首次上报尺寸
    reportSize();

    // 图片/字体等异步资源加载完成后再次上报
    window.addEventListener('load', reportSize);

    // ResizeObserver 监听内容变化（脚本动态修改 DOM 时也能及时上报）
    if (window.ResizeObserver && document.body) {
      var ro = new ResizeObserver(function() { reportSize(); });
      ro.observe(document.body);
    }
  });
})();
`
}

// ============= 构建 iframe srcdoc =============

/**
 * 构建完整的 HTML 文档字符串（用于 iframe.srcdoc）
 */
function buildIframeDoc(content: string, shimScript: string, statusYaml: string): string {
  const isFullDoc = /^\s*<!DOCTYPE/i.test(content) || /^\s*<html/i.test(content)

  // 片段样式：用于非完整文档时设置基础排版
  const fragmentBaseStyle = `
    <style>
      html { margin: 0; padding: 0; box-sizing: border-box; }
      body {
        margin: 0;
        padding: 16px;
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
        background: transparent;
        color: rgba(255, 255, 255, 0.9);
        font-size: 14px;
        line-height: 1.6;
        box-sizing: border-box;
        /* display: inline-block 使 body 宽度由内容决定而非受 iframe 视口约束 */
        display: inline-block;
        min-width: 100vw;
      }
      * { box-sizing: border-box; }
      img { height: auto; }
    </style>
  `

  // YAML 数据内联 script 标签（兜底）
  const yamlDataTag = statusYaml
    ? `<script id="yaml-data-source" type="text/yaml">${statusYaml.replace(/<\/script>/gi, '<\\/script>')}<\/script>`
    : ''

  // shim 内联（避免 CDN 依赖）
  const inlineScripts = `<script>${shimScript}<\/script>`

  if (isFullDoc) {
    // 完整 HTML 文档（游戏/前端卡）：只注入 shim，不覆盖游戏自有布局样式
    // 优先注入到 </head>，如无 </head> 则注入到 </body> 前
    // 注意：使用 function 替换（而非 string 替换），防止 injected 中的 $ 字符
    // 被 String.replace() 解读为反向引用（$1/$&/$` 等），导致 shim 注入内容被污染。
    const injected = `${yamlDataTag}${inlineScripts}`
    if (/<\/head>/i.test(content)) {
      return content.replace(/(<\/head>)/i, (_, head) => `${injected}${head}`)
    }
    if (/<\/body>/i.test(content)) {
      return content.replace(/(<\/body>)/i, (_, body) => `${injected}${body}`)
    }
    return content + injected
  }

  const bodyContent = /^\s*<body/i.test(content)
    ? content
    : `<body>${content}</body>`

  return `<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  ${fragmentBaseStyle}
  ${yamlDataTag}
  ${inlineScripts}
</head>
${bodyContent}
</html>`
}

// ============= 组件 =============

export default function StatusBarIframe({
  rawMessage,
  allMessages = [],
  messageIndex = 0,
  htmlContent = '',
  statusYaml = '',
  minHeight = 200,
  parentOrigin,
  onAction,
}: StatusBarIframeProps) {
  const iframeRef = useRef<HTMLIFrameElement>(null)
  const [iframeSize, setIframeSize] = useState<{ width: number; height: number } | null>(null)

  /**
   * 冻结 allMessages：组件挂载时取快照，之后不再跟随父组件更新。
   * 原因：每当对话新增消息时父组件的 allMessages 引用会重建，若历史消息的 iframe
   * 跟随更新，会触发 srcdoc 重置 + setIframeSize(null)，导致已渲染的 HTML 内容消失后重建（闪烁）。
   * 历史消息的 HTML 渲染不依赖后续新增消息的内容，冻结快照即可。
   */
  const frozenAllMessages = useRef<string[]>(allMessages)

  useEffect(() => {
    if (!iframeRef.current) return

    // parentOrigin is kept as a prop for future use (e.g. if we add non-sandboxed iframes)
    // For the current sandboxed iframe, postMessage uses '*' — see buildShimScript.

    const shim = buildShimScript(rawMessage, frozenAllMessages.current, messageIndex)
    const content = htmlContent || buildDefaultYamlRenderer(statusYaml)
    const doc = buildIframeDoc(content, shim, statusYaml)

    const iframe = iframeRef.current
    iframe.srcdoc = doc

    // 重置尺寸（避免切换内容时残留旧尺寸）
    setIframeSize(null)
  }, [rawMessage, messageIndex, htmlContent, statusYaml, minHeight, parentOrigin])

  // 监听 iframe 内通过 postMessage 上报的消息
  useEffect(() => {
    const handleMessage = (event: MessageEvent) => {
      // 安全检查：只接受来自本 iframe 的消息（srcdoc 的 origin 是 'null'，用 source 做身份校验）
      if (event.source !== iframeRef.current?.contentWindow) return
      const msg = event.data
      if (!msg) return

      if (msg.type === 'resize') {
        const { width, height } = msg.data as { width: number; height: number }
        if (typeof width === 'number' && typeof height === 'number') {
          setIframeSize({
            width: Math.max(width, 0),
            height: Math.max(height, minHeight),
          })
        }
        return
      }

      // 操作类消息：转发给父组件
      if (onAction) {
        if (msg.type === 'fillInput') {
          const text = (msg.data as { text?: string })?.text ?? ''
          onAction('fillInput', text)
        } else if (msg.type === 'playerAction') {
          const action = (msg.data as { action?: string })?.action ?? ''
          onAction('playerAction', action)
        } else if (msg.type === 'triggerAction') {
          const command = (msg.data as { command?: string })?.command ?? ''
          onAction('triggerAction', command)
        } else if (msg.type === 'setChatMessage') {
          const { content, messageId } = msg.data as { content?: string; messageId?: number }
          onAction('setChatMessage', content ?? '', messageId)
        }
      }
    }

    window.addEventListener('message', handleMessage)
    return () => window.removeEventListener('message', handleMessage)
  }, [minHeight, parentOrigin, onAction])

  // 计算实际宽高：有内容尺寸时用内容尺寸，否则用 minHeight / 撑满父容器
  const frameHeight = iframeSize ? iframeSize.height : minHeight
  // 宽度：若内容比父容器宽则横向滚动，否则撑满父容器
  const frameWidth = iframeSize ? iframeSize.width : undefined

  return (
    <div
      className="border border-white/10 rounded-lg bg-black/20 overflow-auto"
      style={{ maxWidth: '100%', width: '100%' }}
    >
      <iframe
        ref={iframeRef}
        // allow-scripts: 运行 JS
        // allow-same-origin: 允许访问父页面 window（ST 兼容：卡片脚本通过 window.parent 存取全局状态）
        // allow-forms: 游戏内表单交互
        // allow-pointer-lock: 鼠标锁定（部分游戏需要）
        // allow-modals: alert/confirm/prompt（已由 shim 覆盖，保留作备用）
        // allow-downloads: 游戏存档下载
        sandbox="allow-scripts allow-same-origin allow-forms allow-pointer-lock allow-modals allow-downloads"
        allow="autoplay"
        style={{
          width: frameWidth ? `${frameWidth}px` : '100%',
          height: `${frameHeight}px`,
          border: 'none',
          display: 'block',
          minWidth: '100%',
        }}
        title="status-bar"
      />
    </div>
  )
}

// ============= 默认 YAML 渲染器模板 =============

/**
 * 当没有提供自定义 HTML 时，使用内置 YAML 状态栏渲染器。
 * 在父页面中使用 js-yaml 解析 YAML，再将渲染好的 HTML 注入 iframe。
 */
function buildDefaultYamlRenderer(statusYaml: string): string {
  const base = `
<body>
  <style>
    .status-block { background: rgba(0,0,0,0.3); border-radius: 12px; padding: 16px; border: 1px solid rgba(255,255,255,0.1); }
    .status-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid rgba(255,255,255,0.1); }
    .status-title { font-size: 16px; font-weight: 600; color: rgba(157,124,245,1); }
    .status-info { font-size: 13px; color: rgba(255,255,255,0.7); margin: 4px 0; }
    .character-card { background: rgba(0,0,0,0.2); border-radius: 8px; padding: 12px; margin: 8px 0; border: 1px solid rgba(255,255,255,0.05); }
    .character-name { font-weight: 600; font-size: 15px; margin-bottom: 8px; color: rgba(157,124,245,1); }
    .attribute { margin: 4px 0; font-size: 13px; line-height: 1.5; }
    .attribute-key { color: rgba(157,124,245,0.8); font-weight: 500; }
    .option-item { padding: 8px 12px; margin: 4px 0; background: rgba(157,124,245,0.1); border-radius: 6px; cursor: pointer; transition: all 0.2s; border-left: 2px solid rgba(157,124,245,0.3); }
    .option-item:hover { background: rgba(157,124,245,0.2); border-left-color: rgba(157,124,245,0.7); }
    .loading { padding: 20px; text-align: center; color: rgba(255,255,255,0.5); }
  </style>
`

  const emptyHtml = `${base}
  <div class="loading">状态栏数据为空</div>
</body>`

  if (!statusYaml || !statusYaml.trim()) return emptyHtml

  let data: unknown
  try {
    data = parseYaml(statusYaml)
  } catch (e: any) {
    return `${base}
  <div class="loading" style="color:rgba(255,100,100,0.8)">YAML 解析失败: ${String(
    e?.message ?? e
  )}</div>
</body>`
  }

  if (!data || typeof data !== 'object') {
    return emptyHtml
  }

  const escHtml = (s: unknown) =>
    String(s)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')

  const obj = data as Record<string, unknown>
  const rootKey = Object.keys(obj)[0]
  if (!rootKey) return emptyHtml

  const block = obj[rootKey] as any
  let html = `${base}
  <div class="status-block">
    <div class="status-header"><div class="status-title">${escHtml(rootKey)}</div></div>
`

  const appendBlock = (key: string, value: any) => {
    if (typeof value === 'string' || typeof value === 'number') {
      html += `    <div class="status-info"><span class="attribute-key">${escHtml(
        key
      )}:</span> ${escHtml(value)}</div>\n`
    } else if (Array.isArray(value)) {
      html += `    <div style="margin-top:8px"><div class="attribute-key">${escHtml(key)}</div>\n`
      value.forEach((item) => {
        if (item && typeof item === 'object') {
          const rec = item as Record<string, any>
          const name = rec['名字'] ?? rec['name'] ?? ''
          html += '      <div class="character-card">\n'
          if (name) {
            html += `        <div class="character-name">${escHtml(name)}</div>\n`
          }
          Object.entries(rec).forEach(([k, v]) => {
            if (k === '名字' || k === 'name') return
            html += `        <div class="attribute"><span class="attribute-key">${escHtml(
              k
            )}:</span> ${escHtml(String(v))}</div>\n`
          })
          html += '      </div>\n'
        } else {
          html += `      <div class="attribute">${escHtml(String(item))}</div>\n`
        }
      })
      html += '    </div>\n'
    } else if (value && typeof value === 'object') {
      const rec = value as Record<string, any>
      const options = rec['选项']
      if (Array.isArray(options)) {
        const name = rec['名字'] ?? key
        html +=
          '    <div style="margin-top:12px"><div style="font-weight:600;margin-bottom:8px;color:rgba(157,124,245,1)">'
        html += `${escHtml(String(name))} 的行动选项</div>\n`
        options.forEach((opt) => {
          html += `      <div class="option-item" onclick="window.onPlayerAction && window.onPlayerAction(${escHtml(
            JSON.stringify(opt)
          )})">${escHtml(opt)}</div>\n`
        })
        html += '    </div>\n'
      } else {
        html += '    <div class="character-card">\n'
        html += `      <div class="character-name">${escHtml(key)}</div>\n`
        Object.entries(rec).forEach(([k, v]) => {
          html += `      <div class="attribute"><span class="attribute-key">${escHtml(
            k
          )}:</span> ${escHtml(String(v))}</div>\n`
        })
        html += '    </div>\n'
      }
    }
  }

  if (block && typeof block === 'object') {
    Object.entries(block as Record<string, any>).forEach(([k, v]) => appendBlock(k, v))
  }

  html += '  </div>\n</body>'
  return html
}
