import React, { useEffect, useRef, useState } from 'react';
import * as captchaApi from '../api/captcha.js';
import { useDialog } from '../lib/utils.js';

// ── 模块级单例：showCaptcha 挂起请求，由 CaptchaModalHost 消费 ──
let pending = null;   // { action, type, resolve }
let listener = null;

function notifyHost() {
  if (listener) listener();
}

/**
 * 验证码流程入口（迁移自 public/js/captcha.js）：
 *  - null    无需验证 / 用户取消
 *  - object  { type: 'proof' | 'recaptcha' | 'turnstile', value }
 *            proof      → 内置验证码通过，value 为后端签发的 captcha_proof
 *            recaptcha  → reCAPTCHA 验证通过，value 为 siteverify token
 *            turnstile  → Turnstile 验证通过，value 为 siteverify token
 * 调用方提交时按 type 放入 captcha_proof / recaptcha_token / turnstile_token 字段。
 */
export function showCaptcha(action) {
  return new Promise((resolve) => {
    captchaApi.required(action)
      .then((r) => {
        if (!r || !r.required || r.type === 'none') { resolve(null); return; }
        pending = { action, type: r.type || 'builtin', resolve };
        notifyHost();
      })
      .catch(() => resolve(null));
  });
}

// 执行并清空排队中的渲染回调（供 onload 全局与竞态兜底共用）
function flushCallbacks(type) {
  const key = type === 'recaptcha' ? 'recaptchaCallbacks' : 'turnstileCallbacks';
  const q = window[key] || [];
  window[key] = [];
  q.forEach((cb) => { try { cb(); } catch { /* 单条失败不影响其余 */ } });
}

// 加载第三方验证码脚本（recaptcha / turnstile）
// 注意：脚本通过 ?onload=onTurnstileLoad / onRecaptchaLoad 回调通知就绪，
// 必须在注入脚本之前定义好全局 onload 函数（事件丢失 → 排队回调永不触发 → 永久卡「正在加载」）。
function ensureThirdPartyScript(type) {
  if (type === 'recaptcha') {
    window.recaptchaCallbacks = window.recaptchaCallbacks || [];
    if (!window.onRecaptchaLoad) {
      window.onRecaptchaLoad = () => flushCallbacks('recaptcha');
    }
    if (typeof window.grecaptcha === 'undefined' && !document.querySelector('script[src*="recaptcha/api"]')) {
      const s = document.createElement('script');
      s.src = 'https://www.recaptcha.net/recaptcha/api.js?onload=onRecaptchaLoad&render=explicit';
      s.async = true; s.defer = true;
      document.head.appendChild(s);
    }
  } else {
    window.turnstileCallbacks = window.turnstileCallbacks || [];
    if (!window.onTurnstileLoad) {
      window.onTurnstileLoad = () => flushCallbacks('turnstile');
    }
    if (typeof window.turnstile === 'undefined' && !document.querySelector('script[src*="turnstile"]')) {
      const s = document.createElement('script');
      s.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onTurnstileLoad&render=explicit';
      s.async = true; s.defer = true;
      document.head.appendChild(s);
    }
  }
}

// 第三方验证码加载超时（毫秒）：脚本注入后若库仍未就绪，自动降级到内置验证码
const THIRD_PARTY_LOAD_TIMEOUT = 8000;

// 注册渲染回调：库就绪立即执行，未就绪排队等 onload 冲刷；加 300ms 竞态兜底
// （脚本恰好在「检查就绪」与「入队」之间加载完成、onload 已错过时重放队列）。
// 脚本注入后启动 8s 加载超时：届时库仍未就绪 → 调用 onTimeout 触发降级；
// 库已就绪但回调错过 → 冲刷队列兜底（wrappedRender 执行并自行清理计时器）。
// 返回清理函数：移除排队回调并清除 300ms 竞态与 8s 超时两个计时器，
// 供弹窗关闭/重开时防泄漏、防残留降级状态。
function queueRender(type, render, onTimeout) {
  const lib = type === 'recaptcha' ? 'grecaptcha' : 'turnstile';
  if (typeof window[lib] !== 'undefined') { render(); return null; }
  const key = type === 'recaptcha' ? 'recaptchaCallbacks' : 'turnstileCallbacks';
  window[key] = window[key] || [];

  let done = false; // 已渲染 / 已超时降级 / 已清理：任一后不再执行 render
  let raceTimer = null;
  let timeoutTimer = null;

  const wrappedRender = () => {
    if (done) return;
    done = true;
    clearTimeout(raceTimer);
    clearTimeout(timeoutTimer);
    render();
  };

  window[key].push(wrappedRender);
  ensureThirdPartyScript(type);

  // 300ms 竞态兜底：脚本恰好在「检查就绪」与「入队」之间完成加载、onload 已错过时重放队列
  raceTimer = setTimeout(() => {
    if (typeof window[lib] !== 'undefined') flushCallbacks(type);
  }, 300);

  // 8s 加载超时：库仍未就绪 → 降级；就绪但回调错过 → 冲刷队列兜底
  timeoutTimer = setTimeout(() => {
    if (done) return;
    if (typeof window[lib] !== 'undefined') {
      flushCallbacks(type);
    } else {
      done = true;
      clearTimeout(raceTimer);
      if (onTimeout) onTimeout();
    }
  }, THIRD_PARTY_LOAD_TIMEOUT);

  return () => {
    if (done) return;
    done = true;
    clearTimeout(raceTimer);
    clearTimeout(timeoutTimer);
    const q = window[key];
    if (q) {
      const i = q.indexOf(wrappedRender);
      if (i >= 0) q.splice(i, 1);
    }
  };
}

// SHA-256（Proof of Work 使用）
async function sha256(str) {
  const buf = new TextEncoder().encode(str);
  const hash = await crypto.subtle.digest('SHA-256', buf);
  return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, '0')).join('');
}

// 内置 SVG 验证码弹窗（notice 为可选提示，用于第三方降级时告知用户）
function BuiltinCaptcha({ onFinish, notice }) {
  const [svg, setSvg] = useState('');
  const [error, setError] = useState('');
  const [answer, setAnswer] = useState('');
  const [powDone, setPowDone] = useState(false);
  const [verifying, setVerifying] = useState(false);
  const tokenRef = useRef(null);
  const imgRef = useRef(null);

  const loadImage = async () => {
    setError('');
    setAnswer('');
    const data = await captchaApi.getImage();
    if (data && data.token) {
      tokenRef.current = data.token;
      setSvg(data.svg);
    }
  };

  useEffect(() => {
    loadImage();
    // 智能验证（Proof of Work）后台运行，20s 内找到满足难度的 nonce
    (async () => {
      try {
        const chal = await captchaApi.powChallenge();
        if (!chal || !chal.token) return;
        const target = '0'.repeat(chal.difficulty);
        const start = Date.now();
        let nonce = 0;
        while (Date.now() - start < 20000) {
          const hash = await sha256(chal.prefix + nonce);
          if (hash.startsWith(target)) {
            await captchaApi.powVerify(chal.token, String(nonce));
            setPowDone(true);
            return;
          }
          nonce++;
        }
      } catch { /* 失败不影响主流程 */ }
    })();
  }, []);

  // 让后端返回的 SVG 自适应弹窗宽度（照搬 captcha.js 的样式处理）
  useEffect(() => {
    if (svg && imgRef.current) {
      const s = imgRef.current.querySelector('svg');
      if (s) s.setAttribute('style', 'width:100%;max-width:240px;height:auto;border-radius:8px;display:block');
    }
  }, [svg]);

  const submit = async () => {
    if (!powDone) { setError('智能验证尚未完成，请稍候...'); return; }
    if (!answer.trim() || !tokenRef.current) { setError('请输入验证码'); return; }
    setVerifying(true);
    const v = await captchaApi.verify(tokenRef.current, answer.trim());
    setVerifying(false);
    if (v.success) {
      onFinish({ type: 'proof', value: v.proof || '' });
    } else {
      setError(v.error || '验证码错误');
      tokenRef.current = null;
      setAnswer('');
      loadImage();
    }
  };

  return (
    <div className="dialog" style={{ maxWidth: 380, textAlign: 'center' }}>
      <h3 style={{ marginBottom: 12 }}>验证码</h3>
      {notice && (
        <p
          role="status"
          aria-live="polite"
          style={{
            color: 'var(--md-ref-on-surface-variant)',
            fontSize: 13,
            lineHeight: 1.5,
            margin: '0 0 12px',
            padding: '8px 12px',
            background: 'var(--md-ref-surface-container-high)',
            borderRadius: 8,
          }}
        >
          {notice}
        </p>
      )}
      <div ref={imgRef} style={{ margin: '0 auto 12px', maxWidth: 280, minHeight: 72 }}>
        {svg
          ? <div dangerouslySetInnerHTML={{ __html: svg }} />
          : <div className="spinner" style={{ width: 24, height: 24, margin: '16px auto' }} />}
      </div>
      <div style={{ display: 'flex', gap: 8, alignItems: 'center', justifyContent: 'center' }}>
        <input
          type="text"
          value={answer}
          onChange={(e) => setAnswer(e.target.value)}
          onKeyDown={(e) => { if (e.key === 'Enter') submit(); }}
          placeholder="输入验证码"
          aria-label="输入验证码"
          maxLength={6}
          autoComplete="off"
          style={{ flex: 1, textAlign: 'center', fontSize: 20, letterSpacing: 6, textTransform: 'uppercase' }}
        />
        <button type="button" className="btn btn-icon" title="刷新" aria-label="刷新验证码" onClick={loadImage} style={{ flexShrink: 0 }}>
          <span className="material-icons">refresh</span>
        </button>
      </div>
      {error && <div role="alert" style={{ color: 'var(--md-ref-error)', fontSize: 13, marginTop: 8 }}>{error}</div>}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6, marginTop: 8, fontSize: 13, color: 'var(--md-ref-on-surface-variant)' }}>
        {powDone ? (
          <>
            <span className="material-icons" style={{ fontSize: 16, color: '#4caf50' }}>check_circle</span>
            <span style={{ color: '#4caf50' }}>智能验证通过</span>
          </>
        ) : (
          <>
            <span className="material-icons" style={{ fontSize: 16 }}>smart_toy</span>
            <span>智能验证中...</span>
          </>
        )}
      </div>
      <div className="actions" style={{ justifyContent: 'center', marginTop: 16 }}>
        <button type="button" className="btn btn-text" onClick={() => onFinish(null)}>取消</button>
        <button type="button" className="btn btn-filled" onClick={submit} disabled={verifying}>确认</button>
      </div>
    </div>
  );
}

// 第三方验证码（reCAPTCHA / Turnstile），验证通过后把 siteverify token 传回调用方。
// 脚本 8s 内加载不出来时自动降级到内置 SVG 验证码（proof 路径），不阻塞用户操作。
function ThirdPartyCaptcha({ type, onFinish }) {
  const siteKey = type === 'recaptcha'
    ? (window._recaptchaSiteKey || '')
    : (window._turnstileSiteKey || '');
  const [status, setStatus] = useState('正在加载...');
  const [degraded, setDegraded] = useState(false);
  const widgetRef = useRef(null);
  const doneRef = useRef(false);

  useEffect(() => {
    if (!siteKey) { onFinish(null); return; }
    const container = widgetRef.current;
    const render = () => {
      if (doneRef.current) return; // 弹窗已取消/卸载或已降级，不再向已移除的容器渲染
      try {
        if (type === 'recaptcha') {
          const wid = window.grecaptcha.render(container, {
            sitekey: siteKey,
            callback: () => {
              setStatus('验证通过');
              let token = '';
              try { token = window.grecaptcha.getResponse(wid); } catch { /* 忽略 */ }
              setTimeout(() => onFinish({ type: 'recaptcha', value: token || '' }), 300);
            },
            'expired-callback': () => setStatus('验证已过期'),
          });
        } else {
          const wid = window.turnstile.render(container, {
            sitekey: siteKey,
            callback: () => {
              setStatus('验证通过');
              let token = '';
              try { token = window.turnstile.getResponse(wid); } catch { /* 忽略 */ }
              setTimeout(() => onFinish({ type: 'turnstile', value: token || '' }), 300);
            },
            'expired-callback': () => setStatus('验证已过期'),
          });
        }
        setStatus('请完成验证');
      } catch {
        setStatus('加载失败');
        setTimeout(() => onFinish(null), 2000);
      }
    };
    // 就绪立即渲染；未就绪排队等 onload 冲刷（含 300ms 竞态兜底）
    // 8s 加载超时 → 自动降级到内置验证码，避免长时间卡「正在加载」阻塞登录
    const cleanupQueue = queueRender(type, render, () => {
      if (doneRef.current) return;
      doneRef.current = true;
      setDegraded(true);
    });
    // 卸载/重开时：防止重复回调，清除超时计时器并移除排队回调（防泄漏/防残留降级状态）
    return () => {
      doneRef.current = true;
      if (cleanupQueue) cleanupQueue();
    };
  }, []);

  // 降级：第三方脚本长时间加载不出来，复用内置 SVG 验证码完整流程
  // （校验仍走 loadImage/verify，调用方拿到 { type: 'proof', value }）
  if (degraded) {
    return (
      <BuiltinCaptcha
        onFinish={onFinish}
        notice="第三方验证码加载失败，已切换为内置验证码"
      />
    );
  }

  return (
    <div className="dialog" style={{ maxWidth: 400, textAlign: 'center' }}>
      <h3 style={{ marginBottom: 16 }}>{type === 'recaptcha' ? 'Google reCAPTCHA' : 'Cloudflare Turnstile'}</h3>
      <div ref={widgetRef} style={{ display: 'flex', justifyContent: 'center', margin: '16px 0' }} />
      <p className="text-muted" style={{ fontSize: 13 }}>{status}</p>
      <div className="actions" style={{ justifyContent: 'center' }}>
        <button type="button" className="btn btn-text" onClick={() => onFinish(null)}>取消</button>
      </div>
    </div>
  );
}

/**
 * 全局挂载的验证码弹窗宿主：放入 Layout 内，配合 showCaptcha(action) 使用。
 */
export default function CaptchaModalHost() {
  const [request, setRequest] = useState(null);

  useEffect(() => {
    listener = () => setRequest(pending);
    return () => { listener = null; };
  }, []);

  const finish = (proof) => {
    if (request) {
      const resolve = request.resolve;
      pending = null;
      setRequest(null);
      resolve(proof);
    }
  };

  // 弹窗键盘/焦点管理（B3）：Esc 取消、聚焦首个输入、关闭后焦点还给触发元素
  const { dialogRef, onKeyDown } = useDialog(!!request, () => finish(null));

  if (!request) return null;

  return (
    <div
      ref={dialogRef}
      className="dialog-overlay active"
      role="dialog"
      aria-modal="true"
      aria-label="验证码"
      style={{ display: 'flex', zIndex: 9999 }}
      onMouseDown={(e) => { if (e.target === e.currentTarget) finish(null); }}
      onKeyDown={onKeyDown}
    >
      {request.type === 'recaptcha' ? (
        <ThirdPartyCaptcha type="recaptcha" onFinish={finish} />
      ) : request.type === 'turnstile' ? (
        <ThirdPartyCaptcha type="turnstile" onFinish={finish} />
      ) : (
        <BuiltinCaptcha onFinish={finish} />
      )}
    </div>
  );
}
