import { getWechatJsSdkSignature } from '../api/client';

type WxCallback<T = Record<string, unknown>> = T & {
  errMsg?: string;
};

type WxSdk = {
  config: (options: {
    debug: boolean;
    appId: string;
    timestamp: number;
    nonceStr: string;
    signature: string;
    jsApiList: string[];
  }) => void;
  ready: (callback: () => void) => void;
  error: (callback: (res: WxCallback) => void) => void;
  checkJsApi: (options: {
    jsApiList: string[];
    success?: (res: WxCallback<{ checkResult?: Record<string, boolean> | string }>) => void;
    fail?: (res: WxCallback) => void;
  }) => void;
  startRecord: (options?: Record<string, unknown>) => void;
  stopRecord: (options: {
    success?: (res: WxCallback<{ localId: string }>) => void;
    fail?: (res: WxCallback) => void;
    cancel?: (res: WxCallback) => void;
  }) => void;
  onVoiceRecordEnd: (options: {
    complete: (res: WxCallback<{ localId: string }>) => void;
  }) => void;
  translateVoice: (options: {
    localId: string;
    isShowProgressTips?: 0 | 1;
    success?: (res: WxCallback<{ translateResult: string }>) => void;
    fail?: (res: WxCallback) => void;
  }) => void;
};

declare global {
  interface Window {
    wx?: WxSdk;
  }
}

const JWEIXIN_SRC = 'https://res.wx.qq.com/open/js/jweixin-1.6.0.js';

let scriptPromise: Promise<void> | null = null;
let configuredUrl = '';
let readyUrl = '';
let configPromise: Promise<void> | null = null;
let autoStopHandler: ((localId: string) => void) | null = null;

function loadWechatScript() {
  if (window.wx) return Promise.resolve();
  if (scriptPromise) return scriptPromise;
  scriptPromise = new Promise<void>((resolve, reject) => {
    const existing = document.querySelector<HTMLScriptElement>(`script[src="${JWEIXIN_SRC}"]`);
    if (existing) {
      existing.addEventListener('load', () => resolve(), { once: true });
      existing.addEventListener('error', () => reject(new Error('微信 JS-SDK 加载失败')), { once: true });
      return;
    }
    const script = document.createElement('script');
    script.src = JWEIXIN_SRC;
    script.async = true;
    script.onload = () => resolve();
    script.onerror = () => reject(new Error('微信 JS-SDK 加载失败'));
    document.head.appendChild(script);
  });
  return scriptPromise;
}

function currentSignatureUrl() {
  return window.location.href.split('#')[0];
}

async function ensureWechatConfigured() {
  const url = currentSignatureUrl();
  if (window.wx && configPromise && configuredUrl === url) return configPromise;

  await loadWechatScript();
  if (!window.wx) throw new Error('微信 JS-SDK 不可用');

  configuredUrl = url;
  configPromise = new Promise<void>(async (resolve, reject) => {
    try {
      const signature = await getWechatJsSdkSignature(url);
      let settled = false;
      window.wx?.ready(() => {
        if (settled) return;
        settled = true;
        readyUrl = url;
        resolve();
      });
      window.wx?.error((res) => {
        if (settled) return;
        settled = true;
        readyUrl = '';
        reject(new Error(res.errMsg || '微信 JS-SDK 验证失败'));
      });
      window.wx?.config({
        debug: false,
        appId: signature.appId,
        timestamp: signature.timestamp,
        nonceStr: signature.nonceStr,
        signature: signature.signature,
        jsApiList: signature.jsApiList,
      });
    } catch (err) {
      reject(err);
    }
  });

  return configPromise;
}

function formatWxError(res: WxCallback | null | undefined, fallback: string) {
  const errMsg = String(res?.errMsg ?? '').trim();
  if (!errMsg) return fallback;
  if (/cancel/i.test(errMsg)) return '已取消语音输入';
  if (/startRecord:fail/i.test(errMsg)) {
    return `微信录音启动失败：${errMsg}`;
  }
  if (/permission|auth|denied/i.test(errMsg)) return '微信语音权限不可用，请检查授权';
  return errMsg;
}

export function prepareWechatVoiceSdk() {
  return ensureWechatConfigured();
}

export function isWechatVoiceReady() {
  return Boolean(window.wx && readyUrl === currentSignatureUrl());
}

export async function checkWechatVoiceApis() {
  await ensureWechatConfigured();
  if (!window.wx) throw new Error('微信 JS-SDK 不可用');
  return new Promise<Record<string, boolean>>((resolve, reject) => {
    window.wx?.checkJsApi({
      jsApiList: ['startRecord', 'stopRecord', 'onVoiceRecordEnd', 'translateVoice'],
      success: (res) => {
        const raw = res.checkResult;
        if (typeof raw === 'string') {
          try {
            resolve(JSON.parse(raw));
          } catch {
            resolve({});
          }
          return;
        }
        resolve(raw || {});
      },
      fail: (res) => reject(new Error(formatWxError(res, '微信语音接口检查失败'))),
    });
  });
}

export function startWechatVoiceRecord(onAutoStop: (localId: string) => void) {
  if (!isWechatVoiceReady()) {
    return Promise.reject(new Error('微信语音初始化中，请稍后再按住说话'));
  }
  if (!window.wx) throw new Error('微信 JS-SDK 不可用');
  autoStopHandler = onAutoStop;
  window.wx.onVoiceRecordEnd({
    complete: (res) => {
      if (res.localId) autoStopHandler?.(res.localId);
    },
  });
  return new Promise<void>((resolve, reject) => {
    let settled = false;
    const settle = (fn: () => void) => {
      if (settled) return;
      settled = true;
      fn();
    };
    window.wx?.startRecord({
      success: () => settle(resolve),
      fail: (res: WxCallback) => settle(() => reject(new Error(formatWxError(res, '微信录音启动失败')))),
      cancel: (res: WxCallback) => settle(() => reject(new Error(formatWxError(res, '已取消语音输入')))),
    });
  });
}

export async function stopWechatVoiceRecord() {
  await ensureWechatConfigured();
  if (!window.wx) throw new Error('微信 JS-SDK 不可用');
  autoStopHandler = null;
  return new Promise<string>((resolve, reject) => {
    window.wx?.stopRecord({
      success: (res) => {
        if (!res.localId) {
          reject(new Error('未录制到语音'));
          return;
        }
        resolve(res.localId);
      },
      fail: (res) => reject(new Error(formatWxError(res, '微信录音停止失败'))),
      cancel: (res) => reject(new Error(formatWxError(res, '已取消语音输入'))),
    });
  });
}

export async function translateWechatVoice(localId: string) {
  await ensureWechatConfigured();
  if (!window.wx) throw new Error('微信 JS-SDK 不可用');
  return new Promise<string>((resolve, reject) => {
    window.wx?.translateVoice({
      localId,
      isShowProgressTips: 1,
      success: (res) => resolve(String(res.translateResult ?? '').trim()),
      fail: (res) => reject(new Error(formatWxError(res, '微信语音转文字失败'))),
    });
  });
}
