import type { TableColumnType } from 'antdv-next';

import type { QqbotApi } from '#/api/qqbot';
import type {
  NapcatLoginNewDeviceStatus,
  QqbotNapcatApi,
} from '#/api/qqbot/napcat';
import type {
  KtTableApi,
  KtTableButton,
  KtTableRowAction,
} from '#/components/ktTable';

import { computed, defineComponent, onBeforeUnmount, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';

import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';

import { useQRCode } from '@vueuse/integrations/useQRCode';
import {
  Alert,
  Button,
  message,
  Space,
  Steps,
  Tag,
  Typography,
} from 'antdv-next';

import { useVbenForm } from '#/adapter/form';
import {
  createQqbotAccount,
  deleteQqbotAccount,
  getQqbotAccountList,
  kickQqbotAccount,
  updateQqbotAccount,
} from '#/api/qqbot';
import {
  cancelQqbotAccountScan,
  getNapcatLoginProgressLabel,
  getNapcatNewDeviceStatusMessage,
  getQqbotAccountScanEventsUrl,
  getQqbotAccountScanStatus,
  mergeNapcatAccountScanResult,
  refreshQqbotAccountScanQrcode,
  resolveNapcatLoginDisplayQrcode,
  startQqbotAccountScanCreate,
  startQqbotAccountScanRefresh,
  submitQqbotAccountScanCaptcha,
} from '#/api/qqbot/napcat';
import { KtTable, useKtTable } from '#/components/ktTable';

const AKtTable = KtTable as any;
const AButton = Button as any;
const ASteps = Steps as any;
const ATypographyLink = Typography.Link as any;
const ATypographyText = Typography.Text as any;

type TencentCaptchaResult = {
  appid?: string;
  errorCode?: number;
  errorMessage?: string;
  randstr?: string;
  ret: number;
  ticket?: string;
};

type TencentCaptchaInstance = {
  destroy: () => void;
  show: () => void;
};

declare global {
  interface Window {
    TencentCaptcha?: new (
      appid: string,
      callback: (res: TencentCaptchaResult) => void,
      options?: Record<string, unknown>,
    ) => TencentCaptchaInstance;
  }
}

let tencentCaptchaScriptPromise: Promise<void> | undefined;

export default defineComponent({
  name: 'QqBotAccountList',
  setup() {
    const editingId = ref<string>();
    const router = useRouter();
    const scanLoading = ref(false);
    const scanQrcodeImageFailed = ref(false);
    const scanQrcodeRevision = ref(0);
    const scanQrcodeText = ref('');
    const scanEvents = ref<QqbotNapcatApi.AccountScanEvent[]>([]);
    const scanState = reactive<{
      captchaUrl?: string;
      containerId?: string;
      containerName?: string;
      deviceVerifyUrl?: string;
      errorMessage?: string;
      expiresAt?: number;
      mode: 'create' | 'refresh';
      newDeviceQrcode?: string;
      newDeviceStatus?: NapcatLoginNewDeviceStatus;
      selfId?: string;
      sessionId?: string;
      status: 'error' | 'expired' | 'idle' | 'pending' | 'success';
      webuiPort?: null | number;
    }>({
      mode: 'create',
      status: 'idle',
    });
    const scanQrcode = useQRCode(scanQrcodeText, {
      errorCorrectionLevel: 'H',
      margin: 2,
      scale: 8,
    });
    const scanQrcodeImageSrc = computed(() => {
      const qrcode = scanQrcodeText.value.trim();
      if (!qrcode) return '';
      if (!scanQrcodeImageFailed.value && isQrcodeImageCandidate(qrcode)) {
        return normalizeQrcodeImageSrc(qrcode, scanQrcodeRevision.value);
      }
      return scanQrcode.value;
    });
    const scanQrcodeOpenHref = computed(() => {
      const qrcode = scanQrcodeText.value.trim();
      if (!qrcode) return '';
      if (isQrcodeImageCandidate(qrcode)) {
        return normalizeQrcodeImageSrc(qrcode, scanQrcodeRevision.value);
      }
      return qrcode;
    });
    const scanProgressItems = computed(() =>
      scanEvents.value.map((event) => ({
        description: formatEventTime(event.createdAt),
        status: getScanStepStatus(event.status),
        title: getNapcatLoginProgressLabel(event),
      })),
    );
    const scanProgressCurrent = computed(() =>
      Math.max(scanProgressItems.value.length - 1, 0),
    );
    const scanQrcodePlaceholderText = computed(() => {
      if (scanState.newDeviceStatus) {
        return getNapcatNewDeviceStatusMessage(scanState.newDeviceStatus);
      }
      if (scanState.captchaUrl) {
        return '等待安全验证';
      }
      if (
        scanState.mode === 'refresh' &&
        scanState.errorMessage?.includes('正在尝试快速登录')
      ) {
        return '正在尝试快速登录';
      }
      if (
        scanState.mode === 'refresh' &&
        scanState.errorMessage?.includes('正在尝试密码登录')
      ) {
        return '正在尝试密码登录';
      }
      return '二维码生成中';
    });
    let scanTimer: number | undefined;
    let scanEventSessionId = '';
    let scanEventSource: EventSource | undefined;

    const [AccountForm, accountFormApi] = useVbenForm({
      commonConfig: {
        labelClass: 'w-24',
      },
      layout: 'horizontal',
      schema: [
        {
          component: 'Input',
          componentProps: {
            placeholder: 'NapCat 当前登录 QQ',
          },
          fieldName: 'selfId',
          label: 'Self ID',
          rules: 'required',
        },
        {
          component: 'Input',
          componentProps: {
            placeholder: '便于后台识别',
          },
          fieldName: 'name',
          label: '账号名称',
        },
        {
          component: 'InputPassword',
          componentProps: () => ({
            placeholder: editingId.value
              ? '留空表示不修改'
              : 'OneBot 反向 WS token',
          }),
          fieldName: 'accessToken',
          label: 'Token',
        },
        {
          component: 'InputPassword',
          componentProps: () => ({
            placeholder: editingId.value
              ? '留空表示不修改 QQ 登录密码'
              : '可选，用于 NapCat 密码登录',
          }),
          fieldName: 'loginPassword',
          label: '登录密码',
        },
        {
          component: 'Switch',
          fieldName: 'enabled',
          label: '启用',
        },
        {
          component: 'Input',
          fieldName: 'remark',
          label: '备注',
        },
      ],
      showDefaultActions: false,
      wrapperClass: 'grid-cols-1',
    });

    const columns: Array<TableColumnType<QqbotApi.Account>> = [
      { dataIndex: 'selfId', key: 'selfId', title: 'Self ID', width: 140 },
      { dataIndex: 'name', key: 'name', title: '账号名称', width: 150 },
      {
        dataIndex: 'connectStatus',
        key: 'accountOnlineStatus',
        title: 'OneBot 连接',
        width: 140,
      },
      {
        dataIndex: 'napcat',
        key: 'qqLoginStatus',
        title: 'QQ 登录',
        width: 150,
      },
      {
        dataIndex: 'napcat',
        key: 'napcatRuntime',
        title: 'NapCat 运行',
        width: 240,
      },
      {
        dataIndex: 'lastHeartbeatAt',
        key: 'lastHeartbeatAt',
        title: '最近活动',
        width: 190,
      },
      {
        dataIndex: 'lastError',
        key: 'runtimeSummary',
        title: '运行说明',
        width: 220,
      },
    ];

    const api: KtTableApi<QqbotApi.Account> = {
      list: async (params) => await getQqbotAccountList(params),
    };
    const buttons: Array<KtTableButton<QqbotApi.Account>> = [
      {
        icon: <Plus class="kt-table__button-icon" />,
        key: 'scanCreate',
        label: '扫码新增账号',
        onClick: openScanCreate,
        permissionCodes: ['QqBot:Account:Create'],
        type: 'primary',
      },
      {
        key: 'manualCreate',
        label: '手动维护',
        onClick: openCreate,
        permissionCodes: ['QqBot:Account:Create'],
      },
    ];
    const rowActions: Array<KtTableRowAction<QqbotApi.Account>> = [
      {
        key: 'config',
        label: '配置',
        onClick: openConfig,
        permissionCodes: ['QqBot:Account:Config'],
      },
      {
        key: 'refreshLogin',
        label: '更新登录',
        onClick: openScanRefresh,
        permissionCodes: ['QqBot:Account:RefreshLogin'],
      },
      {
        confirm: (row) =>
          `确认删除账号「${row.selfId}」吗？该操作会同时删除该账号专属的 NapCat 容器。`,
        danger: true,
        key: 'delete',
        label: '删除',
        onClick: async (row, context) => {
          const result = await deleteQqbotAccount(row.id);
          message.success(
            result.deletedContainers > 0
              ? `账号删除成功，已删除 ${result.deletedContainers} 个 NapCat 容器`
              : '账号删除成功',
          );
          await context.reload();
        },
        permissionCodes: ['QqBot:Account:Delete'],
      },
      {
        disabled: (row) => getOneBotStatus(row) !== 'online',
        key: 'kick',
        label: '断开',
        onClick: async (row, context) => {
          await kickQqbotAccount(row.selfId);
          message.success('连接已断开');
          await context.reload();
        },
        permissionCodes: ['QqBot:Account:Kick'],
      },
      {
        key: 'edit',
        label: '编辑',
        onClick: openEdit,
        permissionCodes: ['QqBot:Account:Edit'],
      },
    ];
    const [registerTable, tableApi] = useKtTable<QqbotApi.Account>({
      api,
      buttons,
      columns,
      formOptions: {
        schema: [
          {
            component: 'Input',
            componentProps: { allowClear: true, placeholder: 'Self ID' },
            fieldName: 'selfId',
            label: 'Self ID',
          },
          {
            component: 'Input',
            componentProps: { allowClear: true, placeholder: '账号名称' },
            fieldName: 'name',
            label: '账号名称',
          },
          {
            component: 'Select',
            componentProps: {
              allowClear: true,
              options: [
                { label: 'OneBot 在线', value: 'online' },
                { label: 'OneBot 离线', value: 'offline' },
              ],
            },
            fieldName: 'connectStatus',
            label: 'OneBot',
          },
        ],
      },
      rowActions,
      tableTitle: 'QQBot 账号连接',
    });
    const modalTitle = computed(() =>
      editingId.value ? '编辑账号' : '新建账号',
    );
    const scanTitle = computed(() =>
      scanState.mode === 'refresh' ? '更新账号登录' : '扫码新增账号',
    );

    const [ScanModal, scanModalApi] = useVbenModal({
      class: 'w-[520px]',
      fullscreenButton: false,
      onBeforeClose() {
        cleanupScanSession();
        return true;
      },
      onCancel() {
        closeScanModal();
      },
    });

    const [AccountModal, accountModalApi] = useVbenModal({
      class: 'w-[620px]',
      fullscreenButton: false,
      async onConfirm() {
        await submitAccount();
      },
      onOpenChange(isOpen: boolean) {
        if (!isOpen) return;
        const { values } = accountModalApi.getData<{
          values?: QqbotApi.AccountBody;
        }>();
        void resetAccountForm(values || getAccountFormDefaults());
      },
    });
    onBeforeUnmount(() => {
      stopScanPolling();
      stopScanEvents();
    });

    async function openScanCreate() {
      await startScan('create');
    }

    async function openScanRefresh(row: QqbotApi.Account) {
      await startScan('refresh', row);
    }

    async function startScan(
      mode: 'create' | 'refresh',
      row?: QqbotApi.Account,
    ) {
      resetScanState(mode);
      scanModalApi.setState({ title: scanTitle.value }).open();
      scanLoading.value = true;
      try {
        if (mode === 'create') {
          await applyScanResult(await startQqbotAccountScanCreate(), {
            reloadQrcode: true,
          });
          return;
        }
        if (!row) {
          message.warning('请选择需要更新登录的账号');
          return;
        }
        await applyScanResult(await startQqbotAccountScanRefresh(row.id), {
          reloadQrcode: true,
        });
      } catch (error) {
        stopScanPolling();
        scanState.status = 'error';
        scanState.errorMessage = getErrorMessage(error);
      } finally {
        scanLoading.value = false;
      }
    }

    async function applyScanResult(
      result: QqbotNapcatApi.AccountScanResult,
      options: { reloadQrcode?: boolean } = {},
    ) {
      const nextState = mergeNapcatAccountScanResult(scanState, result);
      scanState.captchaUrl = nextState.captchaUrl;
      scanState.containerId = nextState.containerId;
      scanState.containerName = nextState.containerName;
      scanState.deviceVerifyUrl = nextState.deviceVerifyUrl;
      scanState.errorMessage = nextState.errorMessage;
      scanState.expiresAt = nextState.expiresAt;
      scanState.mode = nextState.mode;
      scanState.newDeviceQrcode = nextState.newDeviceQrcode;
      scanState.newDeviceStatus = nextState.newDeviceStatus;
      scanState.selfId = nextState.selfId;
      scanState.sessionId = nextState.sessionId;
      scanState.status = nextState.status;
      scanState.webuiPort = nextState.webuiPort;
      const nextQrcode = resolveNapcatLoginDisplayQrcode(nextState);
      const qrcodeChanged = nextQrcode !== scanQrcodeText.value;
      if (qrcodeChanged) {
        scanQrcodeImageFailed.value = false;
      }
      scanQrcodeText.value = nextQrcode;
      if (nextQrcode && (qrcodeChanged || options.reloadQrcode)) {
        scanQrcodeRevision.value += 1;
        scanQrcodeImageFailed.value = false;
      }

      if (result.status === 'pending') {
        startScanPolling();
        startScanEvents(result.sessionId);
        return;
      }
      stopScanPolling();
      stopScanEvents();
      if (result.status === 'success') {
        message.success(
          result.selfId ? `账号 ${result.selfId} 登录态已更新` : '账号已更新',
        );
        await scanModalApi.close();
        await tableApi.reload();
      }
    }

    async function pollScanStatus() {
      if (!scanState.sessionId || scanLoading.value) return;
      scanLoading.value = true;
      try {
        await applyScanResult(
          await getQqbotAccountScanStatus(scanState.sessionId),
        );
      } finally {
        scanLoading.value = false;
      }
    }

    async function refreshScanQrcode() {
      if (!scanState.sessionId) return;
      scanLoading.value = true;
      try {
        await applyScanResult(
          await refreshQqbotAccountScanQrcode(scanState.sessionId),
          { reloadQrcode: true },
        );
      } finally {
        scanLoading.value = false;
      }
    }

    async function submitScanCaptcha() {
      const sessionId = scanState.sessionId;
      const captchaUrl = scanState.captchaUrl;
      if (!sessionId || !captchaUrl || scanLoading.value) {
        return;
      }
      scanLoading.value = true;
      try {
        const captcha = await requestTencentCaptcha(captchaUrl);
        if (
          scanState.sessionId !== sessionId ||
          scanState.captchaUrl !== captchaUrl
        ) {
          return;
        }
        await applyScanResult(
          await submitQqbotAccountScanCaptcha({
            ...captcha,
            sessionId,
          }),
        );
      } catch (error) {
        const text = getErrorMessage(error);
        if (text !== '已取消安全验证') {
          message.error(text);
        }
      } finally {
        scanLoading.value = false;
      }
    }

    function startScanPolling() {
      if (scanTimer) return;
      scanTimer = window.setInterval(() => {
        void pollScanStatus();
      }, 2000);
    }

    function stopScanPolling() {
      if (!scanTimer) return;
      window.clearInterval(scanTimer);
      scanTimer = undefined;
    }

    function startScanEvents(sessionId?: string) {
      if (!sessionId || scanEventSessionId === sessionId) return;
      stopScanEvents();
      scanEventSessionId = sessionId;
      const source = new EventSource(getQqbotAccountScanEventsUrl(sessionId), {
        withCredentials: true,
      });
      scanEventSource = source;
      source.addEventListener('message', (event) => {
        handleScanEvent(event.data);
      });
      source.addEventListener('error', () => {
        stopScanEvents();
      });
    }

    function stopScanEvents() {
      if (scanEventSource) {
        scanEventSource.close();
      }
      scanEventSource = undefined;
      scanEventSessionId = '';
    }

    function handleScanEvent(payload: string) {
      try {
        const event = JSON.parse(payload) as QqbotNapcatApi.AccountScanEvent;
        const index = scanEvents.value.findIndex(
          (item) => item.step === event.step,
        );
        if (index === -1) {
          scanEvents.value.push(event);
        } else {
          scanEvents.value.splice(index, 1, event);
        }
        if (scanEvents.value.length > 20) {
          scanEvents.value.splice(0, scanEvents.value.length - 20);
        }
        if (event.result) {
          void applyScanResult(event.result, {
            reloadQrcode: ['new-device-qrcode-ready', 'qrcode-ready'].includes(
              event.step,
            ),
          });
        }
      } catch {
        // Ignore malformed SSE chunks and wait for the next event.
      }
    }

    function resetScanState(mode: 'create' | 'refresh') {
      stopScanPolling();
      stopScanEvents();
      Object.assign(scanState, {
        captchaUrl: undefined,
        containerId: undefined,
        containerName: undefined,
        deviceVerifyUrl: undefined,
        errorMessage: undefined,
        expiresAt: undefined,
        mode,
        newDeviceQrcode: undefined,
        newDeviceStatus: undefined,
        selfId: undefined,
        sessionId: undefined,
        status: 'idle',
        webuiPort: undefined,
      });
      scanQrcodeImageFailed.value = false;
      scanQrcodeRevision.value = 0;
      scanQrcodeText.value = '';
      scanEvents.value = [];
    }

    function cleanupScanSession() {
      const sessionId = scanState.sessionId;
      stopScanPolling();
      stopScanEvents();
      if (sessionId && scanState.status === 'pending') {
        void cancelQqbotAccountScan(sessionId);
      }
    }

    function closeScanModal() {
      void scanModalApi.close();
    }

    function getScanAlertType() {
      if (scanState.status === 'success') return 'success';
      if (scanState.status === 'error') return 'error';
      if (scanState.status === 'expired') return 'warning';
      return 'info';
    }

    function getNewDeviceAlertType(status?: NapcatLoginNewDeviceStatus) {
      if (status === 'failed') return 'error';
      if (status === 'verified') return 'success';
      return 'warning';
    }

    function getScanMessage() {
      if (scanState.status === 'success') return '扫码登录成功';
      if (scanState.status === 'error') {
        return scanState.errorMessage || '扫码登录失败';
      }
      if (scanState.status === 'expired') return '二维码已过期，请刷新二维码';
      if (scanState.errorMessage) return scanState.errorMessage;
      return '请使用目标 QQ 扫码登录，页面会自动轮询登录结果';
    }

    function getErrorMessage(error: unknown) {
      if (error instanceof Error) return error.message;
      if (typeof error === 'string') return error;
      if (error && typeof error === 'object') {
        const record = error as Record<string, unknown>;
        return `${record.msg || record.message || record.err || '扫码登录请求失败'}`;
      }
      return '扫码登录请求失败';
    }

    function getScanStepStatus(
      status: QqbotNapcatApi.AccountScanEvent['status'],
    ) {
      if (status === 'error') return 'error';
      if (status === 'processing') return 'process';
      if (status === 'success') return 'finish';
      return 'wait';
    }

    function formatEventTime(value: number) {
      const date = new Date(value);
      if (Number.isNaN(date.getTime())) return '';
      return date.toLocaleTimeString('zh-CN', { hour12: false });
    }

    function isQrcodeImageCandidate(value: string) {
      return (
        /^data:image\//i.test(value) ||
        /^https?:\/\//i.test(value) ||
        isRawBase64Image(value)
      );
    }

    function normalizeQrcodeImageSrc(value: string, revision = 0) {
      if (isRawBase64Image(value)) {
        return `data:image/png;base64,${value}`;
      }
      if (/^https?:\/\//i.test(value) && revision > 0) {
        return appendQrcodeCacheBuster(value, revision);
      }
      return value;
    }

    function appendQrcodeCacheBuster(value: string, revision: number) {
      try {
        const url = new URL(value);
        url.searchParams.set('_kt_qrcode_v', `${revision}`);
        return url.toString();
      } catch {
        const joiner = value.includes('?') ? '&' : '?';
        return `${value}${joiner}_kt_qrcode_v=${revision}`;
      }
    }

    function isRawBase64Image(value: string) {
      const normalized = value.trim();
      return (
        normalized.startsWith('iVBORw0KGgo') ||
        normalized.startsWith('/9j/') ||
        normalized.startsWith('R0lGOD')
      );
    }

    const renderAccountOnlineStatus = (row: QqbotApi.Account) => {
      if (!row.enabled) {
        return <Tag color="default">已停用</Tag>;
      }
      const online = getOneBotStatus(row) === 'online';
      return (
        <Tag color={online ? 'success' : 'default'}>
          {online ? 'OneBot 在线' : 'OneBot 离线'}
        </Tag>
      );
    };

    const renderQqLoginStatus = (row: QqbotApi.Account) => {
      const meta = getQqLoginStatusMeta(row);
      const message = getQqLoginMessage(row);
      return (
        <Space direction="vertical" size={2}>
          <Tag color={meta.color}>{meta.label}</Tag>
          {message ? (
            <ATypographyText type="secondary">{message}</ATypographyText>
          ) : null}
        </Space>
      );
    };

    const renderNapcatRuntime = (row: QqbotApi.Account) => {
      const napcat = row.napcat;
      const meta = getNapcatStatusMeta(row);
      const webuiMeta = getNapcatWebuiMeta(row);
      return (
        <Space direction="vertical" size={2}>
          <Space size={4} wrap>
            <Tag color={meta.color}>{meta.label}</Tag>
            <Tag color={webuiMeta.color}>{webuiMeta.label}</Tag>
          </Space>
          {napcat?.containerName ? (
            <ATypographyText type="secondary">
              {napcat.containerName}
              {napcat.webuiPort ? `:${napcat.webuiPort}` : ''}
            </ATypographyText>
          ) : null}
        </Space>
      );
    };

    const renderRecentActivity = (row: QqbotApi.Account) => {
      const active = getRecentActivity(row);
      return (
        <Space direction="vertical" size={2}>
          <span>{active.label}</span>
          <ATypographyText type="secondary">
            {active.time || '暂无记录'}
          </ATypographyText>
        </Space>
      );
    };

    const renderRuntimeSummary = (row: QqbotApi.Account) => {
      const summary = getRuntimeSummary(row);
      return (
        <ATypographyText
          title={summary.text}
          type={summary.level === 'warning' ? 'warning' : undefined}
        >
          {summary.text}
        </ATypographyText>
      );
    };

    function getNapcatStatusMeta(row: QqbotApi.Account) {
      const status = row.containerStatus || row.napcat?.containerStatus;
      if (!row.napcat && !status) {
        return { color: 'default', label: '未绑定专属容器' };
      }
      if (!status) {
        return { color: 'warning', label: '容器记录缺失' };
      }
      const statusMap: Record<
        NonNullable<QqbotApi.AccountNapcatRuntime['containerStatus']>,
        { color: string; label: string }
      > = {
        creating: { color: 'processing', label: '容器创建中' },
        error: { color: 'error', label: '容器异常' },
        running: { color: 'success', label: '容器运行中' },
        stopped: { color: 'default', label: '容器已停止' },
      };
      return statusMap[status];
    }

    function getNapcatWebuiMeta(row: QqbotApi.Account) {
      const status = getWebuiStatus(row);
      if (!row.napcat && status === 'unknown') {
        return { color: 'default', label: 'WebUI 未绑定' };
      }
      if (status === 'online') {
        return { color: 'success', label: 'WebUI 可用' };
      }
      if (status === 'offline') {
        return { color: 'error', label: 'WebUI 不可用' };
      }
      return { color: 'default', label: 'WebUI 未检查' };
    }

    function getQqLoginStatusMeta(row: QqbotApi.Account) {
      if (!row.napcat && !row.qqLoginStatus) {
        return { color: 'default', label: '未绑定容器' };
      }
      const statusMap: Record<
        QqbotApi.QqLoginStatus,
        { color: string; label: string }
      > = {
        offline: { color: 'error', label: 'QQ 离线' },
        online: { color: 'success', label: 'QQ 在线' },
        qrcode_expired: { color: 'warning', label: '二维码过期' },
        qrcode_pending: { color: 'processing', label: '等待扫码' },
        unknown: { color: 'default', label: '状态未知' },
      };
      return statusMap[getQqLoginStatus(row)];
    }

    function getOneBotStatus(row: QqbotApi.Account): QqbotApi.OneBotStatus {
      if (row.oneBotStatus) return row.oneBotStatus;
      if (row.napcat?.oneBotOnline !== undefined) {
        return row.napcat.oneBotOnline ? 'online' : 'offline';
      }
      return row.connectStatus === 'online' ? 'online' : 'offline';
    }

    function getWebuiStatus(row: QqbotApi.Account): QqbotApi.WebuiStatus {
      if (row.webuiStatus) return row.webuiStatus;
      if (row.napcat?.webuiOnline === true) return 'online';
      if (row.napcat?.webuiOnline === false) return 'offline';
      return 'unknown';
    }

    function getQqLoginStatus(row: QqbotApi.Account): QqbotApi.QqLoginStatus {
      return row.qqLoginStatus || row.napcat?.qqLoginStatus || 'unknown';
    }

    function getQqLoginMessage(row: QqbotApi.Account) {
      return row.qqLoginMessage ?? row.napcat?.qqLoginMessage;
    }

    function getRecentActivity(row: QqbotApi.Account) {
      const candidates = [
        { label: '最近心跳', value: row.lastHeartbeatAt },
        { label: '最近连接', value: row.lastConnectedAt },
        { label: '最近扫码登录', value: row.napcat?.lastLoginAt },
        { label: '容器启动', value: row.napcat?.lastStartedAt },
      ].filter((item) => item.value);
      const latest = candidates.toSorted(
        (left, right) =>
          new Date(right.value || '').getTime() -
          new Date(left.value || '').getTime(),
      )[0];
      return {
        label: latest?.label || '暂无活动',
        time: latest?.value ? formatDisplayTime(latest.value) : '',
      };
    }

    function getRuntimeSummary(row: QqbotApi.Account) {
      if (!row.enabled) {
        return { level: 'warning', text: '账号已停用' };
      }
      if (row.lastError) {
        return { level: 'warning', text: `账号异常：${row.lastError}` };
      }
      const qqLoginMessage = getQqLoginMessage(row);
      const qqLoginStatus = getQqLoginStatus(row);
      const containerStatus =
        row.containerStatus || row.napcat?.containerStatus;
      if (qqLoginMessage) {
        return {
          level: 'warning',
          text: `QQ 登录：${qqLoginMessage}`,
        };
      }
      if (row.napcat?.lastError) {
        return { level: 'warning', text: `NapCat：${row.napcat.lastError}` };
      }
      if (qqLoginStatus === 'qrcode_expired') {
        return { level: 'warning', text: '二维码已过期，点击更新登录' };
      }
      if (getOneBotStatus(row) === 'online') {
        return { level: 'normal', text: '消息链路可用' };
      }
      if (qqLoginStatus === 'online') {
        return { level: 'warning', text: 'QQ 在线，等待 OneBot 连接' };
      }
      if (containerStatus === 'running') {
        return { level: 'warning', text: 'NapCat 运行中，等待 OneBot 连接' };
      }
      if (containerStatus === 'creating') {
        return { level: 'warning', text: '容器创建中' };
      }
      if (containerStatus === 'stopped') {
        return { level: 'warning', text: '容器已停止' };
      }
      if (!row.napcat) {
        return {
          level: 'warning',
          text: '可更新登录绑定容器',
        };
      }
      return { level: 'normal', text: '暂无异常记录' };
    }

    function formatDisplayTime(value: string) {
      const date = new Date(value);
      if (Number.isNaN(date.getTime())) return value;
      return date.toLocaleString('zh-CN', { hour12: false });
    }

    function getAccountFormDefaults(): QqbotApi.AccountBody {
      return {
        accessToken: '',
        connectionMode: 'reverse-ws',
        enabled: true,
        loginPassword: '',
        name: '',
        remark: '',
        selfId: '',
      };
    }

    async function resetAccountForm(values: QqbotApi.AccountBody) {
      await accountFormApi.resetForm();
      await accountFormApi.setValues(values);
      await accountFormApi.resetValidate();
    }

    function openCreate() {
      editingId.value = undefined;
      accountModalApi.setData({ values: getAccountFormDefaults() }).open();
    }

    function openConfig(row: QqbotApi.Account) {
      void router.push({
        name: 'QqBotAccountConfig',
        query: {
          selfId: row.selfId,
        },
      });
    }

    function openEdit(row: QqbotApi.Account) {
      editingId.value = row.id;
      accountModalApi
        .setData({
          values: {
            accessToken: '',
            connectionMode: row.connectionMode,
            enabled: row.enabled,
            id: row.id,
            loginPassword: '',
            name: row.name,
            remark: row.remark || '',
            selfId: row.selfId,
          },
        })
        .open();
    }

    async function submitAccount() {
      const { valid } = await accountFormApi.validate();
      if (!valid) return;

      const values = await accountFormApi.getValues<QqbotApi.AccountBody>();
      const selfId = values.selfId?.trim();
      if (!selfId) {
        message.warning('请填写 Self ID');
        return;
      }

      accountModalApi.lock();
      try {
        const payload = {
          ...values,
          id: editingId.value,
          selfId,
        };
        if (!payload.accessToken) delete payload.accessToken;
        if (!payload.loginPassword) delete payload.loginPassword;
        await (editingId.value
          ? updateQqbotAccount(payload)
          : createQqbotAccount(payload));
        message.success('账号保存成功');
        await accountModalApi.close();
        await tableApi.reload();
      } finally {
        accountModalApi.unlock();
      }
    }

    return () => (
      <Page autoContentHeight>
        <AKtTable
          onRegister={registerTable}
          v-slots={{
            bodyCell: ({ column, record }: any) => {
              const row = record as QqbotApi.Account;
              if (column.key === 'accountOnlineStatus') {
                return renderAccountOnlineStatus(row);
              }
              if (column.key === 'qqLoginStatus') {
                return renderQqLoginStatus(row);
              }
              if (column.key === 'napcatRuntime') {
                return renderNapcatRuntime(row);
              }
              if (column.key === 'lastHeartbeatAt') {
                return renderRecentActivity(row);
              }
              if (column.key === 'runtimeSummary') {
                return renderRuntimeSummary(row);
              }
              return undefined;
            },
          }}
        />
        <ScanModal
          title={scanTitle.value}
          v-slots={{
            footer: () => [
              <AButton key="close" onClick={closeScanModal}>
                关闭
              </AButton>,
              <AButton
                disabled={
                  !scanState.sessionId ||
                  !!scanState.captchaUrl ||
                  !!scanState.newDeviceStatus
                }
                key="refresh"
                loading={scanLoading.value}
                onClick={refreshScanQrcode}
              >
                刷新二维码
              </AButton>,
              <AButton
                disabled={!scanState.sessionId}
                key="check"
                loading={scanLoading.value}
                onClick={pollScanStatus}
                type="primary"
              >
                检查状态
              </AButton>,
            ],
          }}
        >
          <Space direction="vertical" size={16} style={{ width: '100%' }}>
            <Alert
              message={getScanMessage()}
              showIcon
              type={getScanAlertType() as any}
            />
            {scanState.containerName ? (
              <Alert
                message={`NapCat 容器：${scanState.containerName}${
                  scanState.webuiPort
                    ? `，WebUI 端口：${scanState.webuiPort}`
                    : ''
                }`}
                showIcon
                type="info"
              />
            ) : null}
            {scanState.newDeviceStatus ? (
              <Alert
                description={
                  <Space direction="vertical">
                    <ATypographyText>
                      请使用手机 QQ
                      扫描下方新设备验证二维码，并在手机端确认登录。
                    </ATypographyText>
                    {scanState.deviceVerifyUrl ? (
                      <ATypographyLink
                        href={scanState.deviceVerifyUrl}
                        target="_blank"
                      >
                        打开新设备验证链接
                      </ATypographyLink>
                    ) : null}
                  </Space>
                }
                message={getNapcatNewDeviceStatusMessage(
                  scanState.newDeviceStatus,
                )}
                showIcon
                type={getNewDeviceAlertType(scanState.newDeviceStatus)}
              />
            ) : null}
            {scanState.captchaUrl && !scanState.newDeviceStatus ? (
              <Alert
                description={
                  <Space>
                    <ATypographyText>
                      请在当前页面完成腾讯安全验证，验证结果会自动提交到对应
                      NapCat 容器。
                    </ATypographyText>
                    <AButton
                      loading={scanLoading.value}
                      onClick={submitScanCaptcha}
                      type="primary"
                    >
                      完成安全验证
                    </AButton>
                  </Space>
                }
                message="QQ 密码登录需要安全验证"
                showIcon
                type="warning"
              />
            ) : null}
            {scanProgressItems.value.length > 0 ? (
              <ASteps
                current={scanProgressCurrent.value}
                direction="vertical"
                items={scanProgressItems.value}
                size="small"
              />
            ) : null}
            <div style={{ display: 'flex', justifyContent: 'center' }}>
              {scanQrcodeText.value ? (
                <img
                  alt="qqbot-login-qrcode"
                  onError={() => {
                    if (isQrcodeImageCandidate(scanQrcodeText.value)) {
                      scanQrcodeImageFailed.value = true;
                    }
                  }}
                  src={scanQrcodeImageSrc.value}
                  style={{
                    background: '#fff',
                    borderRadius: '8px',
                    height: '240px',
                    padding: '12px',
                    width: '240px',
                  }}
                />
              ) : (
                <div
                  style={{
                    alignItems: 'center',
                    border: '1px dashed var(--border-color)',
                    borderRadius: '8px',
                    display: 'flex',
                    height: '240px',
                    justifyContent: 'center',
                    width: '240px',
                  }}
                >
                  {scanQrcodePlaceholderText.value}
                </div>
              )}
            </div>
            {scanQrcodeText.value ? (
              <ATypographyLink href={scanQrcodeOpenHref.value} target="_blank">
                打开扫码链接
              </ATypographyLink>
            ) : null}
          </Space>
        </ScanModal>
        <AccountModal title={modalTitle.value}>
          <AccountForm class="mx-2" />
        </AccountModal>
      </Page>
    );
  },
});

function requestTencentCaptcha(
  proofWaterUrl: string,
): Promise<Omit<QqbotNapcatApi.AccountScanCaptchaBody, 'sessionId'>> {
  const params = parseUrlParams(proofWaterUrl);
  const appid = params.aid || '2081081773';
  const sid = params.sid || '';

  return loadTencentCaptchaScript().then(
    () =>
      new Promise((resolve, reject) => {
        if (!window.TencentCaptcha) {
          reject(new Error('腾讯验证码组件加载失败'));
          return;
        }

        let captcha: TencentCaptchaInstance | undefined;
        let settled = false;
        const finish = (
          error?: Error,
          value?: Omit<QqbotNapcatApi.AccountScanCaptchaBody, 'sessionId'>,
        ) => {
          if (settled) return;
          settled = true;
          try {
            captcha?.destroy();
          } catch {
            // The captcha SDK may already have cleaned up its popup.
          }
          if (error) {
            reject(error);
            return;
          }
          if (!value) {
            reject(new Error('腾讯验证码未返回验证结果'));
            return;
          }
          resolve(value);
        };

        captcha = new window.TencentCaptcha(
          appid,
          (res) => {
            if (res.ret === 0 && res.ticket && res.randstr) {
              finish(undefined, {
                randstr: res.randstr,
                sid,
                ticket: res.ticket,
              });
              return;
            }
            finish(new Error('已取消安全验证'));
          },
          {
            enableAged: true,
            login_appid: params.login_appid,
            showHeader: false,
            sid: params.sid,
            type: 'popup',
            uin: params.uin,
          },
        );
        captcha.show();
      }),
  );
}

async function loadTencentCaptchaScript() {
  if (window.TencentCaptcha) return;
  tencentCaptchaScriptPromise =
    tencentCaptchaScriptPromise ||
    loadScriptWithFallback([
      'https://captcha.gtimg.com/TCaptcha.js',
      'https://ssl.captcha.qq.com/TCaptcha.js',
    ]);
  try {
    await tencentCaptchaScriptPromise;
  } catch (error) {
    tencentCaptchaScriptPromise = undefined;
    throw error;
  }
}

async function loadScriptWithFallback(sources: string[]) {
  let lastError: unknown;
  for (const source of sources) {
    try {
      await loadScript(source);
      return;
    } catch (error) {
      lastError = error;
    }
  }
  throw lastError instanceof Error
    ? lastError
    : new Error('腾讯验证码脚本加载失败');
}

function loadScript(source: string) {
  return new Promise<void>((resolve, reject) => {
    const existing = document.querySelector<HTMLScriptElement>(
      `script[src="${source}"]`,
    );
    if (existing) {
      existing.addEventListener('load', () => resolve(), { once: true });
      existing.addEventListener(
        'error',
        () => reject(new Error(`腾讯验证码脚本加载失败：${source}`)),
        { once: true },
      );
      return;
    }

    const script = document.createElement('script');
    script.async = true;
    script.src = source;
    script.addEventListener('load', () => resolve(), { once: true });
    script.addEventListener(
      'error',
      () => reject(new Error(`腾讯验证码脚本加载失败：${source}`)),
      { once: true },
    );
    document.head.append(script);
  });
}

function parseUrlParams(url: string) {
  const params: Record<string, string> = {};
  try {
    const parsed = new URL(url);
    parsed.searchParams.forEach((value, key) => {
      params[key] = value;
    });
    return params;
  } catch {
    const query = url.split('?')[1] || '';
    query.split('&').forEach((pair) => {
      const [key, value = ''] = pair.split('=');
      if (key) params[key] = decodeURIComponent(value);
    });
    return params;
  }
}
