import { useCallback, useEffect, useId, useRef, useState } from 'react';
import QRCode from 'qrcode';
import { pollWechatScanLogin, startWechatScanLogin } from '../api/client';
import { WechatQrDisplay } from './WechatQrDisplay';

const WX_LOGIN_SCRIPT = 'https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js';

type WxLoginCtor = new (config: {
  self_redirect?: boolean;
  id: string;
  appid: string;
  scope: string;
  redirect_uri: string;
  state: string;
  style?: string;
  href?: string;
  stylelite?: number;
}) => void;

let wxLoginScriptPromise: Promise<void> | null = null;

function loadWxLoginScript() {
  if (typeof window === 'undefined') {
    return Promise.reject(new Error('微信扫码组件不可用'));
  }
  if (window.WxLogin) return Promise.resolve();
  if (!wxLoginScriptPromise) {
    wxLoginScriptPromise = new Promise((resolve, reject) => {
      const existing = document.querySelector<HTMLScriptElement>(
        `script[src="${WX_LOGIN_SCRIPT}"]`,
      );
      if (existing) {
        existing.addEventListener('load', () => resolve(), { once: true });
        existing.addEventListener('error', () => reject(new Error('微信扫码组件加载失败')), {
          once: true,
        });
        return;
      }
      const script = document.createElement('script');
      script.src = WX_LOGIN_SCRIPT;
      script.async = true;
      script.onload = () => resolve();
      script.onerror = () => reject(new Error('微信扫码组件加载失败'));
      document.head.appendChild(script);
    });
  }
  return wxLoginScriptPromise;
}

declare global {
  interface Window {
    WxLogin?: WxLoginCtor;
  }
}

export function WechatScanLogin({
  returnTo,
  onBindingGate,
  onComplete,
  onError,
}: {
  returnTo?: string | null;
  onBindingGate: (pendingToken: string) => void;
  onComplete: () => void;
  onError: (message: string) => void;
}) {
  const containerId = useId().replace(/:/g, '');
  const [qrDataUrl, setQrDataUrl] = useState<string | null>(null);
  const [ready, setReady] = useState(false);
  const [loading, setLoading] = useState(true);
  const [expired, setExpired] = useState(false);
  const stateRef = useRef<string | null>(null);
  const pollRef = useRef<number | null>(null);
  const expireRef = useRef<number | null>(null);
  const sessionKeyRef = useRef(0);

  const stopPolling = useCallback(() => {
    if (pollRef.current !== null) {
      window.clearInterval(pollRef.current);
      pollRef.current = null;
    }
    if (expireRef.current !== null) {
      window.clearTimeout(expireRef.current);
      expireRef.current = null;
    }
  }, []);

  const markExpired = useCallback(() => {
    stopPolling();
    setExpired(true);
    setReady(false);
    setQrDataUrl(null);
    const container = document.getElementById(containerId);
    if (container) container.innerHTML = '';
  }, [stopPolling, containerId]);

  const startPolling = useCallback(
    (state: string, expiresInMs: number) => {
      const expiresAt = Date.now() + expiresInMs;
      expireRef.current = window.setTimeout(() => {
        markExpired();
      }, expiresInMs);
      pollRef.current = window.setInterval(() => {
        if (Date.now() > expiresAt) {
          markExpired();
          return;
        }
        void pollWechatScanLogin(state)
          .then((result) => {
            if (result.status === 'pending') return;
            stopPolling();
            if (result.status === 'complete') {
              onComplete();
              return;
            }
            if (result.status === 'binding_gate' && result.pendingToken) {
              onBindingGate(result.pendingToken);
              return;
            }
            if (result.status === 'expired') {
              markExpired();
              return;
            }
            onError(result.message ?? '微信扫码登录失败');
          })
          .catch((err) => {
            stopPolling();
            onError(err instanceof Error ? err.message : '微信扫码登录失败');
          });
      }, 2000);
    },
    [markExpired, stopPolling, onBindingGate, onComplete, onError],
  );

  const start = useCallback(async () => {
    stopPolling();
    setLoading(true);
    setExpired(false);
    setReady(false);
    setQrDataUrl(null);
    sessionKeyRef.current += 1;
    const sessionKey = sessionKeyRef.current;
    const container = document.getElementById(containerId);
    if (container) container.innerHTML = '';

    try {
      const session = await startWechatScanLogin(returnTo ?? undefined);
      if (sessionKey !== sessionKeyRef.current) return;
      stateRef.current = session.state;

      if (session.mode === 'open') {
        await loadWxLoginScript();
        if (sessionKey !== sessionKeyRef.current || !window.WxLogin) return;

        const host = document.getElementById(containerId);
        if (!host) throw new Error('微信扫码容器不可用');
        host.innerHTML = '';
        new window.WxLogin({
          self_redirect: false,
          id: containerId,
          appid: session.openAppId,
          scope: 'snsapi_login',
          redirect_uri: encodeURIComponent(session.redirectUri),
          state: session.state,
          stylelite: 1,
        });
        setReady(true);
      } else {
        const dataUrl = await QRCode.toDataURL(session.qrUrl, {
          margin: 1,
          width: 180,
        });
        if (sessionKey !== sessionKeyRef.current) return;
        setQrDataUrl(dataUrl);
      }

      startPolling(session.state, session.expiresInMs);
    } catch (err) {
      onError(err instanceof Error ? err.message : '无法启动扫码登录');
    } finally {
      if (sessionKey === sessionKeyRef.current) {
        setLoading(false);
      }
    }
  }, [returnTo, containerId, stopPolling, startPolling, onError]);

  useEffect(() => {
    void start();
    return () => stopPolling();
    // 仅 returnTo 变化时重新拉码，避免父组件回调引用变化导致重复请求
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [returnTo]);

  return (
    <WechatQrDisplay
      qrDataUrl={qrDataUrl}
      wxLoginContainerId={qrDataUrl ? undefined : containerId}
      wxLoginReady={ready}
      loading={loading}
      expired={expired}
      onRefresh={() => void start()}
      tip="请使用微信扫一扫，在手机上确认登录"
    />
  );
}
