/// <reference lib="dom" />
import path from 'path';
import { BasePlatformAdapter } from './base.js';
import type {
  AccountProfile,
  PublishParams,
  PublishResult,
  DateRange,
  AnalyticsData,
  CommentData,
  WorkItem,
} from './base.js';
import type { PlatformType, QRCodeInfo, LoginStatusResult } from '@media-manager/shared';
import { logger } from '../../utils/logger.js';

// Python 多平台发布服务配置
const PYTHON_PUBLISH_SERVICE_URL = process.env.PYTHON_PUBLISH_SERVICE_URL || process.env.XHS_SERVICE_URL || 'http://localhost:5005';

// 服务器根目录（用于构造绝对路径）
const SERVER_ROOT = path.resolve(process.cwd());

/**
 * 抖音平台适配器
 */
export class DouyinAdapter extends BasePlatformAdapter {
  readonly platform: PlatformType = 'douyin';
  readonly loginUrl = 'https://creator.douyin.com/';
  readonly publishUrl = 'https://creator.douyin.com/creator-micro/content/upload';

  /**
   * 获取扫码登录二维码
   */
  async getQRCode(): Promise<QRCodeInfo> {
    try {
      await this.initBrowser();

      if (!this.page) throw new Error('Page not initialized');

      // 访问创作者中心
      await this.page.goto(this.loginUrl);

      // 等待二维码出现
      await this.waitForSelector('.qrcode-image', 30000);

      // 获取二维码图片
      const qrcodeImg = await this.page.$('.qrcode-image img');
      const qrcodeUrl = await qrcodeImg?.getAttribute('src');

      if (!qrcodeUrl) {
        throw new Error('Failed to get QR code');
      }

      const qrcodeKey = `douyin_${Date.now()}`;

      return {
        qrcodeUrl,
        qrcodeKey,
        expireTime: Date.now() + 300000, // 5分钟过期
      };
    } catch (error) {
      logger.error('Douyin getQRCode error:', error);
      throw error;
    }
  }

  /**
   * 检查扫码状态
   */
  async checkQRCodeStatus(qrcodeKey: string): Promise<LoginStatusResult> {
    try {
      if (!this.page) {
        return { status: 'expired', message: '二维码已过期' };
      }

      // 检查是否登录成功（URL 变化或出现用户头像）
      const currentUrl = this.page.url();

      if (currentUrl.includes('/creator-micro/home')) {
        // 在判断登录成功之前，先检查是否有二次校验弹框
        // 抖音二次校验弹框特征：标题"身份验证"、选项包含"接收短信验证码"等
        const hasSecondaryVerification = await this.checkSecondaryVerification();
        
        if (hasSecondaryVerification) {
          logger.info('[Douyin] Secondary verification detected, waiting for user to complete');
          return { 
            status: 'scanned', 
            message: '需要二次校验，请在手机上完成身份验证' 
          };
        }

        // 登录成功，获取 cookie
        const cookies = await this.getCookies();
        await this.closeBrowser();

        return {
          status: 'success',
          message: '登录成功',
          cookies,
        };
      }

      // 检查是否有二次校验弹框（扫码后可能直接弹出，URL 还没变化）
      const hasSecondaryVerification = await this.checkSecondaryVerification();
      if (hasSecondaryVerification) {
        logger.info('[Douyin] Secondary verification detected after scan');
        return { 
          status: 'scanned', 
          message: '需要二次校验，请在手机上完成身份验证' 
        };
      }

      // 检查是否扫码
      const scanTip = await this.page.$('.scan-tip');
      if (scanTip) {
        return { status: 'scanned', message: '已扫码，请确认登录' };
      }

      return { status: 'waiting', message: '等待扫码' };
    } catch (error) {
      logger.error('Douyin checkQRCodeStatus error:', error);
      return { status: 'error', message: '检查状态失败' };
    }
  }

  /**
   * 检查是否存在二次校验弹框
   * 抖音登录时可能弹出身份验证弹框，要求短信验证码或刷脸验证
   */
  private async checkSecondaryVerification(): Promise<boolean> {
    if (!this.page) return false;

    try {
      // 检测多种可能的二次校验弹框
      // 1. 身份验证弹框（标题"身份验证"）
      const verifyTitle = this.page.getByText('身份验证', { exact: false });
      const titleCount = await verifyTitle.count().catch(() => 0);
      if (titleCount > 0) {
        const isVisible = await verifyTitle.first().isVisible().catch(() => false);
        if (isVisible) {
          logger.info('[Douyin] Found "身份验证" dialog');
          return true;
        }
      }

      // 2. 检查是否有"接收短信验证码"选项
      const smsOption = this.page.getByText('接收短信验证码', { exact: false });
      const smsCount = await smsOption.count().catch(() => 0);
      if (smsCount > 0) {
        const isVisible = await smsOption.first().isVisible().catch(() => false);
        if (isVisible) {
          logger.info('[Douyin] Found "接收短信验证码" option');
          return true;
        }
      }

      // 3. 检查是否有"手机刷脸验证"选项
      const faceOption = this.page.getByText('手机刷脸验证', { exact: false });
      const faceCount = await faceOption.count().catch(() => 0);
      if (faceCount > 0) {
        const isVisible = await faceOption.first().isVisible().catch(() => false);
        if (isVisible) {
          logger.info('[Douyin] Found "手机刷脸验证" option');
          return true;
        }
      }

      // 4. 检查是否有"发送短信验证"选项
      const sendSmsOption = this.page.getByText('发送短信验证', { exact: false });
      const sendSmsCount = await sendSmsOption.count().catch(() => 0);
      if (sendSmsCount > 0) {
        const isVisible = await sendSmsOption.first().isVisible().catch(() => false);
        if (isVisible) {
          logger.info('[Douyin] Found "发送短信验证" option');
          return true;
        }
      }

      // 5. 检查页面内容中是否包含二次校验关键文本
      const pageContent = await this.page.content().catch(() => '');
      if (pageContent.includes('为保障账号安全') && 
          (pageContent.includes('身份验证') || pageContent.includes('完成身份验证'))) {
        logger.info('[Douyin] Found secondary verification text in page content');
        return true;
      }

      // 6. 检查是否有验证相关的弹框容器
      const verifySelectors = [
        '[class*="verify-modal"]',
        '[class*="identity-verify"]',
        '[class*="second-verify"]',
        '[class*="security-verify"]',
      ];

      for (const selector of verifySelectors) {
        const element = this.page.locator(selector).first();
        const count = await element.count().catch(() => 0);
        if (count > 0) {
          const isVisible = await element.isVisible().catch(() => false);
          if (isVisible) {
            logger.info(`[Douyin] Found verification modal via selector: ${selector}`);
            return true;
          }
        }
      }

      return false;
    } catch (error) {
      logger.error('[Douyin] Error checking secondary verification:', error);
      return false;
    }
  }

  /**
   * 检查登录状态
   */
  async checkLoginStatus(cookies: string): Promise<boolean> {
    try {
      // 使用无头浏览器后台运行
      await this.initBrowser({ headless: true });
      await this.setCookies(cookies);

      if (!this.page) throw new Error('Page not initialized');

      // 访问个人中心
      await this.page.goto('https://creator.douyin.com/creator-micro/home', {
        waitUntil: 'domcontentloaded',
        timeout: 30000,
      });

      // 等待页面稳定
      await this.page.waitForTimeout(3000);

      const url = this.page.url();
      logger.info(`Douyin checkLoginStatus URL: ${url}`);

      // 如果被重定向到登录页面，说明未登录
      const isLoginPage = url.includes('login') ||
        url.includes('passport') ||
        url.includes('sso');

      // 额外检查：页面上是否有登录相关的元素
      if (!isLoginPage) {
        // 检查是否有登录按钮或二维码（说明需要登录）
        const hasLoginButton = await this.page.$('[class*="login"], [class*="qrcode"], .login-btn');
        if (hasLoginButton) {
          logger.info('Douyin: Found login elements on page, cookie may be expired');
          await this.closeBrowser();
          return false;
        }
      }

      await this.closeBrowser();

      return !isLoginPage;
    } catch (error) {
      logger.error('Douyin checkLoginStatus error:', error);
      await this.closeBrowser();
      return false;
    }
  }

  /**
   * 获取账号信息
   * 通过拦截 API 响应获取准确数据
   */
  async getAccountInfo(cookies: string): Promise<AccountProfile> {
    try {
      // 使用无头浏览器后台运行
      await this.initBrowser({ headless: true });
      await this.setCookies(cookies);

      if (!this.page) throw new Error('Page not initialized');

      let accountName = '未知账号';
      let avatarUrl = '';
      let fansCount = 0;
      let worksCount = 0;
      let accountId = '';
      let worksList: WorkItem[] = [];

      // 捕获的 API 数据
      const capturedData: {
        userInfo?: {
          nickname?: string;
          avatar?: string;
          uid?: string;
          fans?: number;
          following?: number;
        };
        dataOverview?: {
          fans_count?: number;
          total_works?: number;
          total_play?: number;
        };
      } = {};

      // 设置 API 响应监听器
      this.page.on('response', async (response) => {
        const url = response.url();
        try {
          // 监听用户信息 API
          if (url.includes('/creator/user/info') ||
            url.includes('/user/info') ||
            url.includes('/passport/sso/check')) {
            const data = await response.json();
            logger.info(`[Douyin API] User info:`, JSON.stringify(data).slice(0, 500));

            const userInfo = data?.user || data?.data?.user || data?.data;
            if (userInfo) {
              capturedData.userInfo = {
                nickname: userInfo.nickname || userInfo.nick_name || userInfo.name,
                avatar: userInfo.avatar_url || userInfo.avatar || userInfo.avatar_larger?.url_list?.[0],
                uid: userInfo.uid || userInfo.user_id || userInfo.sec_uid,
                fans: userInfo.follower_count || userInfo.fans_count,
                following: userInfo.following_count,
              };
              logger.info(`[Douyin API] Captured user:`, capturedData.userInfo);
            }
          }

          // 监听数据概览 API
          if (url.includes('/data/overview') ||
            url.includes('/creator-micro/data') ||
            url.includes('/data_center/overview')) {
            const data = await response.json();
            logger.info(`[Douyin API] Data overview:`, JSON.stringify(data).slice(0, 500));

            if (data?.data) {
              capturedData.dataOverview = {
                fans_count: data.data.fans_count || data.data.follower_count,
                total_works: data.data.total_item_cnt || data.data.works_count || data.data.video_count,
                total_play: data.data.total_play_cnt,
              };
              logger.info(`[Douyin API] Captured data overview:`, capturedData.dataOverview);
            }
          }

          // 监听首页数据
          if (url.includes('/creator-micro/home') && url.includes('api')) {
            const data = await response.json();
            logger.info(`[Douyin API] Home data:`, JSON.stringify(data).slice(0, 500));
          }
        } catch {
          // 忽略非 JSON 响应
        }
      });

      // 访问创作者中心首页
      logger.info('[Douyin] Navigating to creator home...');
      await this.page.goto('https://creator.douyin.com/creator-micro/home', {
        waitUntil: 'networkidle',
        timeout: 30000,
      });

      await this.page.waitForTimeout(4000);

      // 尝试从 Cookie 获取账号ID
      try {
        const cookieList = JSON.parse(cookies);
        const uidCookie = cookieList.find((c: { name: string }) =>
          c.name === 'passport_uid' || c.name === 'uid' || c.name === 'ttwid'
        );
        if (uidCookie) {
          accountId = uidCookie.value;
        }
      } catch { }

      // 使用捕获的 API 数据
      if (capturedData.userInfo) {
        if (capturedData.userInfo.nickname) accountName = capturedData.userInfo.nickname;
        if (capturedData.userInfo.avatar) avatarUrl = capturedData.userInfo.avatar;
        if (capturedData.userInfo.uid) accountId = capturedData.userInfo.uid;
        if (capturedData.userInfo.fans) fansCount = capturedData.userInfo.fans;
      }

      if (capturedData.dataOverview) {
        if (capturedData.dataOverview.fans_count) fansCount = capturedData.dataOverview.fans_count;
        if (capturedData.dataOverview.total_works) worksCount = capturedData.dataOverview.total_works;
      }

      // 如果 API 没捕获到，尝试从页面 DOM 获取
      if (!accountName || accountName === '未知账号') {
        const nameEl = await this.page.$('[class*="nickname"], [class*="userName"], [class*="name"]');
        if (nameEl) {
          const text = await nameEl.textContent();
          if (text?.trim()) accountName = text.trim();
        }
      }

      if (!avatarUrl) {
        const avatarEl = await this.page.$('[class*="avatar"] img');
        if (avatarEl) {
          avatarUrl = await avatarEl.getAttribute('src') || '';
        }
      }

      // 如果还没获取到粉丝数和作品数，从页面元素获取
      if (fansCount === 0 || worksCount === 0) {
        const statsData = await this.page.evaluate(() => {
          const result = { fans: 0, works: 0 };

          // 查找所有包含数字的元素
          const allText = document.body.innerText;

          // 尝试匹配 "粉丝 xxx" 或 "xxx 粉丝"
          const fansMatch = allText.match(/粉丝[：:\s]*(\d+(?:\.\d+)?[万亿]?)|(\d+(?:\.\d+)?[万亿]?)\s*粉丝/);
          if (fansMatch) {
            const numStr = fansMatch[1] || fansMatch[2];
            result.fans = parseFloat(numStr) * (numStr.includes('万') ? 10000 : numStr.includes('亿') ? 100000000 : 1);
          }

          // 尝试匹配作品数
          const worksMatch = allText.match(/作品[：:\s]*(\d+)|(\d+)\s*个?作品|共\s*(\d+)\s*个/);
          if (worksMatch) {
            result.works = parseInt(worksMatch[1] || worksMatch[2] || worksMatch[3]);
          }

          return result;
        });

        if (fansCount === 0 && statsData.fans > 0) fansCount = statsData.fans;
        if (worksCount === 0 && statsData.works > 0) worksCount = statsData.works;
      }

      // 如果没有获取到ID，生成一个
      if (!accountId) {
        accountId = `douyin_${Date.now()}`;
      }

      await this.closeBrowser();

      logger.info(`[Douyin] Account info: ${accountName}, ID: ${accountId}, Fans: ${fansCount}, Works: ${worksCount}`);

      return {
        accountId,
        accountName,
        avatarUrl,
        fansCount,
        worksCount,
        worksList,
      };
    } catch (error) {
      logger.error('Douyin getAccountInfo error:', error);
      await this.closeBrowser();
      // 返回默认值而不是抛出错误
      return {
        accountId: `douyin_${Date.now()}`,
        accountName: '抖音账号',
        avatarUrl: '',
        fansCount: 0,
        worksCount: 0,
      };
    }
  }

  /**
   * 验证码信息类型
   */
  private captchaTypes = {
    SMS: 'sms',      // 短信验证码
    IMAGE: 'image',  // 图形验证码
  } as const;

  /**
   * 处理验证码弹框（支持短信验证码和图形验证码）
   * 优先使用传统方式检测，AI 作为辅助
   * @param onCaptchaRequired 验证码回调
   * @returns 'success' | 'failed' | 'not_needed'
   */
  private async handleCaptchaIfNeeded(
    onCaptchaRequired?: (captchaInfo: {
      taskId: string;
      type: 'sms' | 'image';
      phone?: string;
      imageBase64?: string;
    }) => Promise<string>
  ): Promise<'success' | 'failed' | 'not_needed' | 'need_retry_headful'> {
    if (!this.page) return 'not_needed';

    try {
      // 1. 先使用传统方式检测图形验证码弹框
      logger.info('[Douyin Publish] Checking for captcha...');
      const imageCaptchaResult = await this.handleImageCaptcha(onCaptchaRequired);
      if (imageCaptchaResult !== 'not_needed') {
        logger.info(`[Douyin Publish] Image captcha result: ${imageCaptchaResult}`);
        return imageCaptchaResult;
      }

      // 2. 再检测短信验证码弹框
      const smsCaptchaResult = await this.handleSmsCaptcha(onCaptchaRequired);
      if (smsCaptchaResult !== 'not_needed') {
        logger.info(`[Douyin Publish] SMS captcha result: ${smsCaptchaResult}`);
        return smsCaptchaResult;
      }

      // 3. 如果传统方式没检测到，使用 AI 辅助检测
      const aiStatus = await this.aiAnalyzePublishStatus();
      if (aiStatus?.status === 'need_captcha') {
        logger.info(`[Douyin Publish] AI detected captcha: type=${aiStatus.captchaType}, desc=${aiStatus.captchaDescription}`);

        // AI 检测到验证码，尝试处理
        if (onCaptchaRequired && aiStatus.captchaType) {
          // 获取验证码截图
          const imageBase64 = await this.screenshotBase64();

          try {
            const captchaCode = await onCaptchaRequired({
              taskId: `ai_captcha_${Date.now()}`,
              type: aiStatus.captchaType === 'sms' ? 'sms' : 'image',
              captchaDescription: aiStatus.captchaDescription,
              imageBase64,
            });

            if (captchaCode) {
              // 使用 AI 指导输入验证码
              const guide = await this.aiGetPublishOperationGuide('需要输入验证码');
              if (guide?.hasAction && guide.targetSelector) {
                await this.page.fill(guide.targetSelector, captchaCode);
                await this.page.waitForTimeout(500);

                // 查找确认按钮
                const confirmGuide = await this.aiGetPublishOperationGuide('已输入验证码，需要点击确认按钮');
                if (confirmGuide?.hasAction && confirmGuide.targetSelector) {
                  await this.page.click(confirmGuide.targetSelector);
                  await this.page.waitForTimeout(2000);
                }

                // 验证是否成功
                const afterStatus = await this.aiAnalyzePublishStatus();
                if (afterStatus?.status === 'need_captcha') {
                  logger.warn('[Douyin Publish] AI: Captcha still present after input');
                  return 'failed';
                }
                return 'success';
              }
            }
          } catch (captchaError) {
            logger.error('[Douyin Publish] AI captcha handling failed:', captchaError);
          }
        }

        // 没有回调或处理失败，需要手动介入
        if (this.isHeadless) {
          return 'need_retry_headful';
        }
      }

      return 'not_needed';

    } catch (error) {
      logger.error('[Douyin Publish] Captcha handling error:', error);
      return 'not_needed';
    }
  }

  /**
   * 处理图形验证码
   * @returns 'need_retry_headful' 表示在 headless 模式检测到验证码，需要用 headful 模式重新发布
   */
  private async handleImageCaptcha(
    onCaptchaRequired?: (captchaInfo: {
      taskId: string;
      type: 'sms' | 'image';
      phone?: string;
      imageBase64?: string;
    }) => Promise<string>
  ): Promise<'success' | 'failed' | 'not_needed' | 'need_retry_headful'> {
    if (!this.page) return 'not_needed';

    try {
      // 图形验证码检测 - 使用多种方式检测
      // 标题："请完成身份验证后继续"
      // 提示："为保护帐号安全，请根据图片输入验证码"

      let hasImageCaptcha = false;

      // 方式1: 使用 getByText 直接查找可见的文本
      const captchaTitle = this.page.getByText('请完成身份验证后继续', { exact: false });
      const captchaHint = this.page.getByText('请根据图片输入验证码', { exact: false });

      const titleCount = await captchaTitle.count().catch(() => 0);
      const hintCount = await captchaHint.count().catch(() => 0);

      logger.info(`[Douyin Publish] Image captcha check - title elements: ${titleCount}, hint elements: ${hintCount}`);

      if (titleCount > 0) {
        const isVisible = await captchaTitle.first().isVisible().catch(() => false);
        logger.info(`[Douyin Publish] Image captcha title visible: ${isVisible}`);
        if (isVisible) {
          hasImageCaptcha = true;
        }
      }

      if (!hasImageCaptcha && hintCount > 0) {
        const isVisible = await captchaHint.first().isVisible().catch(() => false);
        logger.info(`[Douyin Publish] Image captcha hint visible: ${isVisible}`);
        if (isVisible) {
          hasImageCaptcha = true;
        }
      }

      // 方式2: 检查页面 HTML 内容作为备用
      if (!hasImageCaptcha) {
        const pageContent = await this.page.content().catch(() => '');
        if (pageContent.includes('请完成身份验证后继续') || pageContent.includes('请根据图片输入验证码')) {
          logger.info('[Douyin Publish] Image captcha text found in page content');
          // 再次尝试使用选择器
          const modalCandidates = [
            'div[class*="modal"]:visible',
            'div[role="dialog"]:visible',
            '[class*="verify-modal"]',
            '[class*="captcha"]',
          ];
          for (const selector of modalCandidates) {
            const modal = this.page.locator(selector).first();
            if (await modal.count().catch(() => 0) > 0 && await modal.isVisible().catch(() => false)) {
              hasImageCaptcha = true;
              logger.info(`[Douyin Publish] Image captcha modal found via: ${selector}`);
              break;
            }
          }
        }
      }

      if (!hasImageCaptcha) {
        return 'not_needed';
      }

      logger.info('[Douyin Publish] Image captcha modal detected!');

      // 截图保存当前状态
      try {
        const screenshotPath = `uploads/debug/image_captcha_${Date.now()}.png`;
        await this.page.screenshot({ path: screenshotPath, fullPage: true });
        logger.info(`[Douyin Publish] Image captcha screenshot saved: ${screenshotPath}`);
      } catch { }

      // 如果当前是 headless 模式，返回特殊状态让调用方用 headful 模式重试
      if (this.isHeadless) {
        logger.info('[Douyin Publish] Captcha detected in HEADLESS mode, need to restart with HEADFUL');
        return 'need_retry_headful';
      }

      // 已经是 headful 模式，通知前端并等待用户完成验证
      logger.info('[Douyin Publish] In HEADFUL mode, waiting for user to complete captcha in browser window...');

      // 通知前端验证码需要手动输入
      if (onCaptchaRequired) {
        const taskId = `captcha_manual_${Date.now()}`;
        onCaptchaRequired({
          taskId,
          type: 'image',
          imageBase64: '',
        }).catch(() => { });
      }

      // 等待验证码弹框消失（用户在浏览器窗口中完成验证）
      const captchaTimeout = 180000; // 3 分钟超时
      const captchaStartTime = Date.now();

      while (Date.now() - captchaStartTime < captchaTimeout) {
        await this.page.waitForTimeout(2000);

        const captchaTitle = this.page.getByText('请完成身份验证后继续', { exact: false });
        const captchaHint = this.page.getByText('请根据图片输入验证码', { exact: false });

        const titleVisible = await captchaTitle.count() > 0 && await captchaTitle.first().isVisible().catch(() => false);
        const hintVisible = await captchaHint.count() > 0 && await captchaHint.first().isVisible().catch(() => false);

        if (!titleVisible && !hintVisible) {
          logger.info('[Douyin Publish] Captcha completed by user!');
          await this.page.waitForTimeout(2000);
          return 'success';
        }

        const elapsed = Math.floor((Date.now() - captchaStartTime) / 1000);
        logger.info(`[Douyin Publish] Waiting for captcha (${elapsed}s)...`);
      }

      logger.error('[Douyin Publish] Captcha timeout');
      return 'failed';

    } catch (error) {
      logger.error('[Douyin Publish] Image captcha handling error:', error);
      return 'not_needed';
    }
  }

  /**
   * 处理短信验证码
   */
  private async handleSmsCaptcha(
    onCaptchaRequired?: (captchaInfo: {
      taskId: string;
      type: 'sms' | 'image';
      phone?: string;
      imageBase64?: string;
    }) => Promise<string>
  ): Promise<'success' | 'failed' | 'not_needed'> {
    if (!this.page) return 'not_needed';

    try {
      // 短信验证码弹框选择器
      const smsCaptchaSelectors = [
        '.second-verify-panel',
        '.uc-ui-verify_sms-verify',
        '.uc-ui-verify-new_header-title:has-text("接收短信验证码")',
        'article.uc-ui-verify_sms-verify',
      ];

      let hasSmsCaptcha = false;
      for (const selector of smsCaptchaSelectors) {
        const element = this.page.locator(selector).first();
        const count = await element.count().catch(() => 0);
        if (count > 0) {
          const isVisible = await element.isVisible().catch(() => false);
          if (isVisible) {
            hasSmsCaptcha = true;
            logger.info(`[Douyin Publish] SMS captcha detected with selector: ${selector}`);
            break;
          }
        }
      }

      if (!hasSmsCaptcha) {
        return 'not_needed';
      }

      logger.info('[Douyin Publish] SMS captcha modal detected!');

      // 截图保存
      try {
        const screenshotPath = `uploads/debug/sms_captcha_${Date.now()}.png`;
        await this.page.screenshot({ path: screenshotPath, fullPage: true });
        logger.info(`[Douyin Publish] SMS captcha screenshot saved: ${screenshotPath}`);
      } catch { }

      // 获取手机号
      let phone = '';
      try {
        const phoneElement = this.page.locator('.var_TextPrimary').first();
        if (await phoneElement.count() > 0) {
          phone = await phoneElement.textContent() || '';
        }
        if (!phone) {
          const pageText = await this.page.locator('.second-verify-panel, .uc-ui-verify_sms-verify').first().textContent() || '';
          const phoneMatch = pageText.match(/1\d{2}\*{4,6}\d{2}/);
          if (phoneMatch) phone = phoneMatch[0];
        }
        logger.info(`[Douyin Publish] Found phone number: ${phone}`);
      } catch { }

      // 点击获取验证码按钮
      const getCaptchaBtnSelectors = [
        '.uc-ui-input_right p:has-text("获取验证码")',
        '.uc-ui-input_right:has-text("获取验证码")',
        'p:has-text("获取验证码")',
      ];

      for (const selector of getCaptchaBtnSelectors) {
        const btn = this.page.locator(selector).first();
        if (await btn.count() > 0 && await btn.isVisible()) {
          try {
            await btn.click();
            logger.info(`[Douyin Publish] Clicked "获取验证码" button: ${selector}`);
            await this.page.waitForTimeout(1500);
            break;
          } catch { }
        }
      }

      if (!onCaptchaRequired) {
        logger.error('[Douyin Publish] SMS captcha required but no callback provided');
        return 'failed';
      }

      const taskId = `captcha_${Date.now()}`;
      logger.info(`[Douyin Publish] Requesting SMS captcha from user, taskId: ${taskId}, phone: ${phone}`);

      const captchaCode = await onCaptchaRequired({
        taskId,
        type: 'sms',
        phone,
      });

      if (!captchaCode) {
        logger.error('[Douyin Publish] No SMS captcha code received');
        return 'failed';
      }

      logger.info(`[Douyin Publish] Received SMS captcha code: ${captchaCode}`);

      // 填写验证码
      const inputSelectors = [
        '.second-verify-panel input[type="number"]',
        '.uc-ui-verify_sms-verify input[type="number"]',
        '.uc-ui-input input[placeholder="请输入验证码"]',
        'input[placeholder="请输入验证码"]',
        '.uc-ui-input_textbox input',
      ];

      let inputFilled = false;
      for (const selector of inputSelectors) {
        const input = this.page.locator(selector).first();
        if (await input.count() > 0 && await input.isVisible()) {
          await input.click();
          await input.fill('');
          await input.type(captchaCode, { delay: 50 });
          inputFilled = true;
          logger.info(`[Douyin Publish] SMS captcha code filled via: ${selector}`);
          break;
        }
      }

      if (!inputFilled) {
        logger.error('[Douyin Publish] SMS captcha input not found');
        return 'failed';
      }

      await this.page.waitForTimeout(500);

      // 点击验证按钮
      const verifyBtnSelectors = [
        '.uc-ui-verify_sms-verify_button:has-text("验证"):not(.disabled)',
        '.uc-ui-button:has-text("验证"):not(.disabled)',
        '.second-verify-panel .uc-ui-button:has-text("验证")',
        'div.uc-ui-button:has-text("验证")',
      ];

      for (const selector of verifyBtnSelectors) {
        const btn = this.page.locator(selector).first();
        if (await btn.count() > 0 && await btn.isVisible()) {
          try {
            const isDisabled = await btn.evaluate((el: HTMLElement) => el.classList.contains('disabled'));
            if (!isDisabled) {
              await btn.click();
              logger.info(`[Douyin Publish] Clicked SMS verify button: ${selector}`);
              break;
            }
          } catch { }
        }
      }

      // 等待结果
      await this.page.waitForTimeout(3000);

      // 检查弹框是否消失
      let stillHasCaptcha = false;
      for (const selector of smsCaptchaSelectors) {
        const element = this.page.locator(selector).first();
        const isVisible = await element.isVisible().catch(() => false);
        if (isVisible) {
          stillHasCaptcha = true;
          break;
        }
      }

      if (!stillHasCaptcha) {
        logger.info('[Douyin Publish] SMS captcha verified successfully');
        return 'success';
      }

      logger.warn('[Douyin Publish] SMS captcha modal still visible');
      return 'failed';

    } catch (error) {
      logger.error('[Douyin Publish] SMS captcha handling error:', error);
      return 'not_needed';
    }
  }

  /**
   * 检查 Python 发布服务是否可用
   */
  private async checkPythonServiceAvailable(): Promise<boolean> {
    try {
      const response = await fetch(`${PYTHON_PUBLISH_SERVICE_URL}/health`, {
        method: 'GET',
        signal: AbortSignal.timeout(3000),
      });
      if (response.ok) {
        const data = await response.json();
        return data.status === 'ok' && data.supported_platforms?.includes('douyin');
      }
      return false;
    } catch {
      return false;
    }
  }

  /**
   * 通过 Python 服务发布视频（带 AI 辅助）
   * @returns PublishResult - 如果需要验证码，返回 { success: false, needCaptcha: true, captchaType: '...' }
   */
  private async publishVideoViaPython(
    cookies: string,
    params: PublishParams,
    onProgress?: (progress: number, message: string) => void,
    onCaptchaRequired?: (captchaInfo: { taskId: string; type: 'sms' | 'image'; phone?: string; imageBase64?: string }) => Promise<string>
  ): Promise<PublishResult & { needCaptcha?: boolean; captchaType?: string }> {
    logger.info('[Douyin Python] Starting publish via Python service with AI assist...');
    onProgress?.(5, '正在通过 Python 服务发布...');

    try {
      // 将相对路径转换为绝对路径
      const absoluteVideoPath = path.isAbsolute(params.videoPath)
        ? params.videoPath
        : path.resolve(SERVER_ROOT, params.videoPath);

      const absoluteCoverPath = params.coverPath
        ? (path.isAbsolute(params.coverPath) ? params.coverPath : path.resolve(SERVER_ROOT, params.coverPath))
        : undefined;

      // 使用 AI 辅助发布接口
      const response = await fetch(`${PYTHON_PUBLISH_SERVICE_URL}/publish/ai-assisted`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          platform: 'douyin',
          cookie: cookies,
          title: params.title,
          description: params.description || params.title,
          video_path: absoluteVideoPath,
          cover_path: absoluteCoverPath,
          tags: params.tags || [],
          post_time: params.scheduledTime ? new Date(params.scheduledTime).toISOString().replace('T', ' ').slice(0, 19) : null,
          location: params.location || '重庆市',
        }),
        signal: AbortSignal.timeout(600000), // 10分钟超时
      });

      const result = await response.json();

      if (result.success) {
        onProgress?.(100, '发布成功');
        logger.info('[Douyin Python] Publish successful');
        return {
          success: true,
          platformVideoId: result.video_id || `douyin_${Date.now()}`,
          videoUrl: result.video_url || '',
        };
      }

      // 如果返回了截图，使用 AI 分析
      if (result.screenshot_base64) {
        logger.info('[Douyin Python] Got screenshot, analyzing with AI...');
        const { aiService } = await import('../../ai/index.js');
        
        if (aiService.isAvailable()) {
          const aiStatus = await aiService.analyzePublishStatus(result.screenshot_base64, 'douyin');
          logger.info(`[Douyin Python] AI analysis: status=${aiStatus.status}, confidence=${aiStatus.confidence}%`);

          // AI 判断发布成功
          if (aiStatus.status === 'success' && aiStatus.confidence >= 70) {
            onProgress?.(100, '发布成功');
            return {
              success: true,
              platformVideoId: `douyin_${Date.now()}`,
              videoUrl: result.video_url || result.page_url || '',
            };
          }

          // AI 检测到需要验证码
          if (aiStatus.status === 'need_captcha') {
            logger.info(`[Douyin Python] AI detected captcha: ${aiStatus.captchaDescription}`);
            
            // 如果有验证码回调，尝试处理
            if (onCaptchaRequired) {
              onProgress?.(50, `AI 检测到验证码: ${aiStatus.captchaDescription || '请输入验证码'}`);
              // 返回需要验证码的状态，让上层处理
              return {
                success: false,
                needCaptcha: true,
                captchaType: aiStatus.captchaType || 'image',
                errorMessage: aiStatus.captchaDescription || '需要验证码',
              };
            }
          }

          // AI 判断发布失败
          if (aiStatus.status === 'failed') {
            throw new Error(aiStatus.errorMessage || 'AI 检测到发布失败');
          }
        }
      }

      // Python 返回需要验证码
      if (result.need_captcha || result.status === 'need_captcha') {
        logger.info(`[Douyin Python] Captcha required: type=${result.captcha_type}`);
        onProgress?.(0, `检测到需要${result.captcha_type || ''}验证码，切换到浏览器模式...`);
        return {
          success: false,
          needCaptcha: true,
          captchaType: result.captcha_type || 'image',
          errorMessage: result.error || `需要验证码`,
        };
      }

      throw new Error(result.error || '发布失败');
    } catch (error) {
      logger.error('[Douyin Python] Publish failed:', error);
      throw error;
    }
  }

  /**
   * 发布视频
   * 参考 https://github.com/kebenxiaoming/matrix 项目实现
   * 只使用 Python 服务发布，如果检测到验证码返回错误让前端用有头浏览器重试
   * @param onCaptchaRequired 验证码回调，返回用户输入的验证码
   * @param options.headless 是否使用无头模式，默认 true
   */
  async publishVideo(
    cookies: string,
    params: PublishParams,
    onProgress?: (progress: number, message: string) => void,
    onCaptchaRequired?: (captchaInfo: { taskId: string; phone?: string }) => Promise<string>,
    options?: { headless?: boolean }
  ): Promise<PublishResult> {
    // 只使用 Python 服务发布
    const pythonAvailable = await this.checkPythonServiceAvailable();
    if (!pythonAvailable) {
      logger.error('[Douyin] Python service not available');
      return {
        success: false,
        errorMessage: 'Python 发布服务不可用，请确保 Python 服务已启动',
      };
    }

    logger.info('[Douyin] Using Python service for publishing');
    try {
      const pythonResult = await this.publishVideoViaPython(cookies, params, onProgress);

      // 检查是否需要验证码 - 返回错误让前端用有头浏览器重试
      if (pythonResult.needCaptcha) {
        logger.info(`[Douyin] Python detected captcha (${pythonResult.captchaType}), need headful browser`);
        onProgress?.(0, `检测到${pythonResult.captchaType}验证码，请使用有头浏览器重试...`);
        return {
          success: false,
          errorMessage: `CAPTCHA_REQUIRED:检测到${pythonResult.captchaType}验证码，需要使用有头浏览器完成验证`,
        };
      }
      
      if (pythonResult.success) {
        return pythonResult;
      }
      
      return {
        success: false,
        errorMessage: pythonResult.errorMessage || '发布失败',
      };
    } catch (pythonError) {
      logger.error('[Douyin] Python publish failed:', pythonError);
      return {
        success: false,
        errorMessage: pythonError instanceof Error ? pythonError.message : '发布失败',
      };
    }

    /* ========== Playwright 方式已注释，只使用 Python API ==========
    try {
      await this.initBrowser({ headless: useHeadless });
      await this.setCookies(cookies);

      if (!useHeadless) {
        logger.info('[Douyin Publish] Running in HEADFUL mode - browser window is visible');
        onProgress?.(1, '已打开浏览器窗口，请注意查看...');
      }

      if (!this.page) throw new Error('Page not initialized');

      // 检查视频文件是否存在
      const fs = await import('fs');
      if (!fs.existsSync(params.videoPath)) {
        throw new Error(`视频文件不存在: ${params.videoPath}`);
      }

      onProgress?.(5, '正在打开上传页面...');
      logger.info(`[Douyin Publish] Starting upload for: ${params.videoPath}`);

      // 访问上传页面
      await this.page.goto(this.publishUrl, {
        waitUntil: 'domcontentloaded',
        timeout: 60000,
      });

      // 等待页面加载
      await this.page.waitForTimeout(3000);
      logger.info(`[Douyin Publish] Page loaded: ${this.page.url()}`);

      onProgress?.(10, '正在选择视频文件...');

      // 上传视频 - 优先使用 AI 截图分析找到上传入口
      let uploadTriggered = false;

      // 方法1: AI 截图分析找到上传入口
      logger.info('[Douyin Publish] Using AI to find upload entry...');
      try {
        const screenshot = await this.screenshotBase64();
        const guide = await aiService.getPageOperationGuide(screenshot, 'douyin', '找到视频上传入口并点击上传按钮');
        logger.info(`[Douyin Publish] AI analysis result:`, guide);

        if (guide.hasAction && guide.targetSelector) {
          logger.info(`[Douyin Publish] AI suggested selector: ${guide.targetSelector}`);
          try {
            const [fileChooser] = await Promise.all([
              this.page.waitForEvent('filechooser', { timeout: 10000 }),
              this.page.click(guide.targetSelector),
            ]);
            await fileChooser.setFiles(params.videoPath);
            uploadTriggered = true;
            logger.info('[Douyin Publish] Upload triggered via AI selector');
          } catch (e) {
            logger.warn(`[Douyin Publish] AI selector click failed: ${e}`);
          }
        }
      } catch (e) {
        logger.warn(`[Douyin Publish] AI analysis failed: ${e}`);
      }

      // 方法2: 尝试点击常见的上传区域触发 file chooser
      if (!uploadTriggered) {
        logger.info('[Douyin Publish] Trying common upload selectors...');
        const uploadSelectors = [
          // 抖音常见上传区域选择器
          'div[class*="container-drag-info"]',
          'div[class*="container-drag"]',
          'div[class*="upload-drag"]',
          'div[class*="drag-info"]',
          'div[class*="upload-btn"]',
          'div[class*="drag-area"]',
          '[class*="upload"] [class*="drag"]',
          'div[class*="upload-area"]',
          '.upload-trigger',
          'button:has-text("上传")',
          'div:has-text("上传视频"):not(:has(div))',
          'span:has-text("点击上传")',
        ];
        
        for (const selector of uploadSelectors) {
          if (uploadTriggered) break;
          try {
            const element = this.page.locator(selector).first();
            if (await element.count() > 0 && await element.isVisible()) {
              logger.info(`[Douyin Publish] Trying selector: ${selector}`);
              const [fileChooser] = await Promise.all([
                this.page.waitForEvent('filechooser', { timeout: 5000 }),
                element.click(),
              ]);
              await fileChooser.setFiles(params.videoPath);
              uploadTriggered = true;
              logger.info(`[Douyin Publish] Upload triggered via selector: ${selector}`);
            }
          } catch (e) {
            // 继续尝试下一个选择器
          }
        }
      }

      // 方法3: 直接设置 file input
      if (!uploadTriggered) {
        logger.info('[Douyin Publish] Trying file input method...');
        const fileInputs = await this.page.$$('input[type="file"]');
        logger.info(`[Douyin Publish] Found ${fileInputs.length} file inputs`);
        for (const fileInput of fileInputs) {
          try {
            const accept = await fileInput.getAttribute('accept');
            if (!accept || accept.includes('video') || accept.includes('*')) {
              await fileInput.setInputFiles(params.videoPath);
              uploadTriggered = true;
              logger.info('[Douyin Publish] Upload triggered via file input');
              break;
            }
          } catch (e) {
            logger.warn(`[Douyin Publish] File input method failed: ${e}`);
          }
        }
      }

      if (!uploadTriggered) {
        // 截图调试
        try {
          if (this.page) {
            const screenshotPath = `uploads/debug/douyin_no_upload_${Date.now()}.png`;
            await this.page.screenshot({ path: screenshotPath, fullPage: true });
            logger.info(`[Douyin Publish] Screenshot saved: ${screenshotPath}`);
          }
        } catch {}
        throw new Error('未找到上传入口');
      }

      onProgress?.(15, '视频上传中，等待跳转到发布页面...');

      // 参考 matrix: 等待页面跳转到发布页面
      // URL: https://creator.douyin.com/creator-micro/content/post/video?enter_from=publish_page
      const maxWaitTime = 180000; // 3分钟
      const startTime = Date.now();
      let lastAiCheckTime = 0;
      const aiCheckInterval = 10000; // 每10秒使用AI检测一次

      while (Date.now() - startTime < maxWaitTime) {
        await this.page.waitForTimeout(2000);
        const currentUrl = this.page.url();

        if (currentUrl.includes('/content/post/video')) {
          logger.info('[Douyin Publish] Entered video post page');
          break;
        }

        // 检查上传进度（通过DOM）
        let progressDetected = false;
        const progressText = await this.page.locator('[class*="progress"]').first().textContent().catch(() => '');
        if (progressText) {
          const match = progressText.match(/(\d+)%/);
          if (match) {
            const progress = parseInt(match[1]);
            onProgress?.(15 + Math.floor(progress * 0.3), `视频上传中: ${progress}%`);
            progressDetected = true;
          }
        }

        // 使用AI检测上传进度（每隔一段时间检测一次）
        if (!progressDetected && Date.now() - lastAiCheckTime > aiCheckInterval) {
          lastAiCheckTime = Date.now();
          try {
            const screenshot = await this.screenshotBase64();
            const uploadStatus = await aiService.analyzeUploadProgress(screenshot, 'douyin');
            logger.info(`[Douyin Publish] AI upload status:`, uploadStatus);
            
            if (uploadStatus.isComplete) {
              logger.info('[Douyin Publish] AI detected upload complete');
              // 继续等待页面跳转
            }
            
            if (uploadStatus.isFailed) {
              throw new Error(`视频上传失败: ${uploadStatus.statusDescription}`);
            }
            
            if (uploadStatus.progress !== null) {
              onProgress?.(15 + Math.floor(uploadStatus.progress * 0.3), `视频上传中: ${uploadStatus.progress}%`);
            }
          } catch (aiError) {
            logger.warn('[Douyin Publish] AI progress check failed:', aiError);
          }
        }

        // 检查是否上传失败
        const failText = await this.page.locator('div:has-text("上传失败")').first().count().catch(() => 0);
        if (failText > 0) {
          throw new Error('视频上传失败');
        }
      }

      if (!this.page.url().includes('/content/post/video')) {
        throw new Error('等待进入发布页面超时');
      }

      onProgress?.(50, '正在填写视频信息...');
      await this.page.waitForTimeout(2000);

      // 参考 matrix: 填充标题
      // 先尝试找到标题输入框
      logger.info('[Douyin Publish] Filling title...');

      // 方式1: 找到 "作品标题" 旁边的 input
      const titleInput = this.page.getByText('作品标题').locator('..').locator('xpath=following-sibling::div[1]').locator('input');
      if (await titleInput.count() > 0) {
        await titleInput.fill(params.title.slice(0, 30));
        logger.info('[Douyin Publish] Title filled via input');
      } else {
        // 方式2: 使用 .notranslate 编辑器（参考 matrix）
        const editorContainer = this.page.locator('.notranslate, [class*="editor"] [contenteditable="true"]').first();
        if (await editorContainer.count() > 0) {
          await editorContainer.click();
          await this.page.keyboard.press('Control+A');
          await this.page.keyboard.press('Backspace');
          await this.page.keyboard.type(params.title, { delay: 30 });
          await this.page.keyboard.press('Enter');
          logger.info('[Douyin Publish] Title filled via editor');
        }
      }

      onProgress?.(60, '正在添加话题标签...');

      // 参考 matrix: 添加话题标签
      // 使用 .zone-container 选择器
      if (params.tags && params.tags.length > 0) {
        const tagContainer = '.zone-container, [class*="mention-container"], [class*="hash-tag"]';

        for (let i = 0; i < params.tags.length; i++) {
          const tag = params.tags[i];
          logger.info(`[Douyin Publish] Adding tag ${i + 1}: ${tag}`);

          try {
            await this.page.type(tagContainer, `#${tag}`, { delay: 50 });
            await this.page.keyboard.press('Space');
            await this.page.waitForTimeout(500);
          } catch (e) {
            // 如果失败，尝试在编辑器中添加
            try {
              await this.page.keyboard.type(` #${tag} `, { delay: 50 });
              await this.page.waitForTimeout(500);
            } catch {
              logger.warn(`[Douyin Publish] Failed to add tag: ${tag}`);
            }
          }
        }
      }

      onProgress?.(70, '等待视频处理完成...');

      // 参考 matrix: 等待 "重新上传" 按钮出现，表示视频上传完成
      const uploadCompleteMaxWait = 600000; // 增加到 10 分钟
      const uploadStartTime = Date.now();
      let videoProcessed = false;

      while (Date.now() - uploadStartTime < uploadCompleteMaxWait) {
        // 检查多种完成标志
        const reuploadCount = await this.page.locator('div').filter({ hasText: '重新上传' }).count().catch(() => 0);
        const replaceCount = await this.page.locator('div:has-text("替换"), button:has-text("替换")').count().catch(() => 0);
        const completeCount = await this.page.locator('[class*="upload-complete"], [class*="upload-success"]').count().catch(() => 0);

        if (reuploadCount > 0 || replaceCount > 0 || completeCount > 0) {
          logger.info('[Douyin Publish] Video upload completed');
          videoProcessed = true;
          break;
        }

        // 检查发布按钮是否可用（也是上传完成的标志）
        const publishBtnEnabled = await this.page.getByRole('button', { name: '发布', exact: true }).isEnabled().catch(() => false);
        if (publishBtnEnabled) {
          logger.info('[Douyin Publish] Publish button is enabled, video should be ready');
          videoProcessed = true;
          break;
        }

        // 检查上传失败
        const failCount = await this.page.locator('div:has-text("上传失败")').count().catch(() => 0);
        if (failCount > 0) {
          throw new Error('视频处理失败');
        }

        const elapsed = Math.floor((Date.now() - uploadStartTime) / 1000);
        logger.info(`[Douyin Publish] Waiting for video processing... (${elapsed}s)`);
        await this.page.waitForTimeout(3000);
        onProgress?.(70 + Math.min(14, Math.floor(elapsed / 20)), `等待视频处理完成 (${elapsed}s)...`);
      }

      if (!videoProcessed) {
        logger.warn('[Douyin Publish] Video processing timeout, but will try to publish anyway');
      }

      // 点击 "我知道了" 弹窗（如果存在）
      const knownBtn = this.page.getByRole('button', { name: '我知道了' });
      if (await knownBtn.count() > 0) {
        await knownBtn.first().click();
        await this.page.waitForTimeout(1000);
      }

      onProgress?.(85, '正在发布...');
      await this.page.waitForTimeout(3000);

      // 参考 matrix: 点击发布按钮
      logger.info('[Douyin Publish] Looking for publish button...');

      // 尝试多种方式找到发布按钮
      let publishClicked = false;

      // 方式1: 使用 getByRole
      const publishBtn = this.page.getByRole('button', { name: '发布', exact: true });
      if (await publishBtn.count() > 0) {
        // 等待按钮可点击
        try {
          await publishBtn.waitFor({ state: 'visible', timeout: 10000 });
          const isEnabled = await publishBtn.isEnabled();
          if (isEnabled) {
            await publishBtn.click();
            publishClicked = true;
            logger.info('[Douyin Publish] Publish button clicked via getByRole');
          }
        } catch (e) {
          logger.warn('[Douyin Publish] getByRole method failed:', e);
        }
      }

      // 方式2: 使用选择器
      if (!publishClicked) {
        const selectors = [
          'button:has-text("发布")',
          '[class*="publish-btn"]',
          'button[class*="primary"]:has-text("发布")',
          '.semi-button-primary:has-text("发布")',
        ];

        for (const selector of selectors) {
          const btn = this.page.locator(selector).first();
          if (await btn.count() > 0) {
            try {
              const isEnabled = await btn.isEnabled();
              if (isEnabled) {
                await btn.click();
                publishClicked = true;
                logger.info(`[Douyin Publish] Publish button clicked via selector: ${selector}`);
                break;
              }
            } catch { }
          }
        }
      }

      if (!publishClicked) {
        // 截图帮助调试
        try {
          const screenshotPath = `uploads/debug/no_publish_btn_${Date.now()}.png`;
          await this.page.screenshot({ path: screenshotPath, fullPage: true });
          logger.info(`[Douyin Publish] Screenshot saved: ${screenshotPath}`);
        } catch { }
        throw new Error('未找到可点击的发布按钮');
      }

      logger.info('[Douyin Publish] Publish button clicked, waiting for result...');

      // 点击发布后截图
      await this.page.waitForTimeout(2000);
      try {
        const screenshotPath = `uploads/debug/after_publish_click_${Date.now()}.png`;
        await this.page.screenshot({ path: screenshotPath, fullPage: true });
        logger.info(`[Douyin Publish] After click screenshot saved: ${screenshotPath}`);
      } catch { }

      // 检查是否有确认弹窗需要处理
      const confirmSelectors = [
        'button:has-text("确认发布")',
        'button:has-text("确定")',
        'button:has-text("确认")',
        '.semi-modal button:has-text("发布")',
        '[class*="modal"] button[class*="primary"]',
      ];

      for (const selector of confirmSelectors) {
        const confirmBtn = this.page.locator(selector).first();
        if (await confirmBtn.count() > 0 && await confirmBtn.isVisible()) {
          logger.info(`[Douyin Publish] Found confirm button: ${selector}`);
          await confirmBtn.click();
          await this.page.waitForTimeout(2000);
          logger.info('[Douyin Publish] Confirm button clicked');
          break;
        }
      }

      // 检查是否需要验证码
      const captchaHandled = await this.handleCaptchaIfNeeded(onCaptchaRequired);
      if (captchaHandled === 'failed') {
        throw new Error('验证码验证失败');
      }
      // 如果检测到验证码，通知前端需要手动验证
      if (captchaHandled === 'need_retry_headful') {
        logger.info('[Douyin Publish] Captcha detected, requesting user to complete verification...');
        onProgress?.(85, '检测到验证码，请在浏览器中完成验证后重试');
        await this.closeBrowser();
        // 返回特殊错误，让前端知道需要手动验证
        return {
          success: false,
          errorMessage: 'CAPTCHA_REQUIRED:检测到验证码，请在浏览器中登录账号完成验证后重试发布',
        };
      }

      onProgress?.(90, '等待发布完成...');

      // 参考 matrix: 等待跳转到管理页面表示发布成功
      // URL: https://creator.douyin.com/creator-micro/content/manage
      const publishMaxWait = 180000; // 3 分钟
      const publishStartTime = Date.now();

      // 记录点击发布时的 URL，用于检测是否跳转
      const publishPageUrl = this.page.url();
      logger.info(`[Douyin Publish] Publish page URL: ${publishPageUrl}`);

      // AI 检测计数器，避免过于频繁
      let aiCheckCounter = 0;
      let lastProgressCheckTime = 0;
      const progressCheckInterval = 5000; // 每5秒检测一次发布进度

      while (Date.now() - publishStartTime < publishMaxWait) {
        await this.page.waitForTimeout(3000);
        const currentUrl = this.page.url();

        const elapsed = Math.floor((Date.now() - publishStartTime) / 1000);
        logger.info(`[Douyin Publish] Waiting for redirect (${elapsed}s), current URL: ${currentUrl}`);

        // 在等待过程中也检测验证码弹框
        const captchaResult = await this.handleCaptchaIfNeeded(onCaptchaRequired);
        if (captchaResult === 'failed') {
          throw new Error('验证码验证失败');
        }
        // 如果检测到验证码，通知前端需要手动验证
        if (captchaResult === 'need_retry_headful') {
          logger.info('[Douyin Publish] Captcha detected in wait loop, requesting user verification...');
          onProgress?.(85, '检测到验证码，请在浏览器中完成验证后重试');
          await this.closeBrowser();
          return {
            success: false,
            errorMessage: 'CAPTCHA_REQUIRED:检测到验证码，请在浏览器中登录账号完成验证后重试发布',
          };
        }

        // 检查是否跳转到管理页面
        if (currentUrl.includes('/content/manage')) {
          logger.info('[Douyin Publish] Redirected to manage page, checking for background upload...');
          
          // 检查是否有后台上传进度条（抖音特有：右下角的上传进度框）
          const uploadProgressBox = await this.page.locator('[class*="upload-progress"], [class*="uploading"], div:has-text("作品上传中"), div:has-text("请勿关闭页面")').count().catch(() => 0);
          const uploadProgressText = await this.page.locator('div:has-text("上传中"), div:has-text("上传完成后")').first().textContent().catch(() => '');
          
          // 如果有后台上传进度，继续等待
          if (uploadProgressBox > 0 || uploadProgressText.includes('上传')) {
            logger.info(`[Douyin Publish] Background upload in progress: ${uploadProgressText}`);
            const match = uploadProgressText.match(/(\d+)%/);
            if (match) {
              const progress = parseInt(match[1]);
              onProgress?.(85 + Math.floor(progress * 0.15), `后台上传中: ${progress}%`);
              if (progress >= 100) {
                logger.info('[Douyin Publish] Background upload complete!');
                onProgress?.(100, '发布成功！');
                await this.closeBrowser();
                return { success: true, videoUrl: currentUrl };
              }
            }
            // 继续等待上传完成
            continue;
          }
          
          // 没有后台上传进度，发布完成
          logger.info('[Douyin Publish] No background upload, publish complete!');
          onProgress?.(100, '发布成功！');
          await this.closeBrowser();
          return {
            success: true,
            videoUrl: currentUrl,
          };
        }

        // 检查发布进度条（DOM方式）
        const publishProgressText = await this.page.locator('[class*="progress"], [class*="loading"], [class*="publishing"]').first().textContent().catch(() => '');
        if (publishProgressText) {
          const match = publishProgressText.match(/(\d+)%/);
          if (match) {
            const progress = parseInt(match[1]);
            onProgress?.(85 + Math.floor(progress * 0.15), `发布中: ${progress}%`);
            logger.info(`[Douyin Publish] Publish progress: ${progress}%`);
          }
        }

        // AI检测发布进度（定期检测）
        if (Date.now() - lastProgressCheckTime > progressCheckInterval) {
          lastProgressCheckTime = Date.now();
          try {
            const screenshot = await this.screenshotBase64();
            const publishStatus = await aiService.analyzePublishProgress(screenshot, 'douyin');
            logger.info(`[Douyin Publish] AI publish progress status:`, publishStatus);
            
            if (publishStatus.isComplete) {
              logger.info('[Douyin Publish] AI detected publish complete');
              onProgress?.(100, '发布成功！');
              await this.closeBrowser();
              return { success: true, videoUrl: this.page.url() };
            }
            
            if (publishStatus.isFailed) {
              throw new Error(`发布失败: ${publishStatus.statusDescription}`);
            }
            
            if (publishStatus.progress !== null) {
              onProgress?.(85 + Math.floor(publishStatus.progress * 0.15), `发布中: ${publishStatus.progress}%`);
            }
            
            if (publishStatus.isPublishing) {
              logger.info(`[Douyin Publish] Still publishing: ${publishStatus.statusDescription}`);
            }
          } catch (aiError) {
            logger.warn('[Douyin Publish] AI publish progress check failed:', aiError);
          }
        }

        // 检查是否有成功提示弹窗（Toast/Modal）
        // 使用更精确的选择器，避免匹配按钮文字
        const successToast = await this.page.locator('.semi-toast-content:has-text("发布成功"), .semi-modal-body:has-text("发布成功"), [class*="toast"]:has-text("发布成功"), [class*="message"]:has-text("发布成功")').count().catch(() => 0);
        if (successToast > 0) {
          logger.info('[Douyin Publish] Found success toast/modal');
          // 等待一下看是否会跳转
          await this.page.waitForTimeout(5000);
          const newUrl = this.page.url();
          if (newUrl.includes('/content/manage')) {
            logger.info('[Douyin Publish] Redirected to manage page after success toast');
            onProgress?.(100, '发布成功！');
            await this.closeBrowser();
            return {
              success: true,
              videoUrl: newUrl,
            };
          }
        }

        // 检查是否有明确的错误提示弹窗
        const errorToast = await this.page.locator('.semi-toast-error, [class*="toast-error"], .semi-modal-body:has-text("失败")').first().textContent().catch(() => '');
        if (errorToast && errorToast.includes('失败')) {
          logger.error(`[Douyin Publish] Error toast found: ${errorToast}`);
          throw new Error(`发布失败: ${errorToast}`);
        }

        // 每隔几次循环使用 AI 辅助检测发布状态
        aiCheckCounter++;
        if (aiCheckCounter >= 3) {
          aiCheckCounter = 0;
          const aiStatus = await this.aiAnalyzePublishStatus();
          if (aiStatus) {
            logger.info(`[Douyin Publish] AI status: ${aiStatus.status}, confidence: ${aiStatus.confidence}%`);

            if (aiStatus.status === 'success' && aiStatus.confidence >= 70) {
              logger.info('[Douyin Publish] AI detected publish success');
              onProgress?.(100, '发布成功！');
              await this.closeBrowser();
              return {
                success: true,
                videoUrl: currentUrl,
              };
            }

            if (aiStatus.status === 'failed' && aiStatus.confidence >= 70) {
              logger.error(`[Douyin Publish] AI detected failure: ${aiStatus.errorMessage}`);
              throw new Error(aiStatus.errorMessage || 'AI 检测到发布失败');
            }

            // AI 建议需要操作
            if (aiStatus.status === 'need_action' && aiStatus.nextAction) {
              logger.info(`[Douyin Publish] AI suggests action: ${aiStatus.nextAction.targetDescription}`);
              const guide = await this.aiGetPublishOperationGuide(aiStatus.pageDescription);
              if (guide?.hasAction) {
                await this.aiExecuteOperation(guide);
              }
            }
          }
        }

        // 更新进度
        onProgress?.(90 + Math.min(9, Math.floor(elapsed / 20)), `等待发布完成 (${elapsed}s)...`);
      }

      // 如果超时，使用 AI 做最后一次状态检查
      const finalUrl = this.page.url();
      logger.info(`[Douyin Publish] Timeout! Final URL: ${finalUrl}`);

      // AI 最终检查
      const finalAiStatus = await this.aiAnalyzePublishStatus();
      if (finalAiStatus) {
        logger.info(`[Douyin Publish] Final AI status: ${finalAiStatus.status}`);

        if (finalAiStatus.status === 'success') {
          onProgress?.(100, '发布成功！');
          await this.closeBrowser();
          return {
            success: true,
            videoUrl: finalUrl,
          };
        }

        if (finalAiStatus.status === 'failed') {
          throw new Error(finalAiStatus.errorMessage || 'AI 检测到发布失败');
        }
      }

      if (finalUrl.includes('/content/manage')) {
        onProgress?.(100, '发布成功！');
        await this.closeBrowser();
        return {
          success: true,
          videoUrl: finalUrl,
        };
      }

      // 截图保存用于调试
      try {
        const screenshotPath = `uploads/debug/publish_timeout_${Date.now()}.png`;
        await this.page.screenshot({ path: screenshotPath, fullPage: true });
        logger.info(`[Douyin Publish] Timeout screenshot saved: ${screenshotPath}`);
      } catch { }

      throw new Error('发布超时，页面未跳转到管理页面，请手动检查是否发布成功');

    } catch (error) {
      logger.error('[Douyin Publish] Error:', error);
      await this.closeBrowser();
      return {
        success: false,
        errorMessage: error instanceof Error ? error.message : '发布失败',
      };
    }
    ========== Playwright 方式已注释结束 ========== */
  }

  /**
   * 通过 Python API 获取评论
   */
  private async getCommentsViaPython(cookies: string, videoId: string): Promise<CommentData[]> {
    logger.info('[Douyin] Getting comments via Python API...');

    const response = await fetch(`${PYTHON_PUBLISH_SERVICE_URL}/comments`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        platform: 'douyin',
        cookie: cookies,
        work_id: videoId,
      }),
    });

    if (!response.ok) {
      throw new Error(`Python API returned ${response.status}`);
    }

    const result = await response.json();

    if (!result.success) {
      throw new Error(result.error || 'Failed to get comments');
    }

    // 转换数据格式
    return (result.comments || []).map((comment: {
      comment_id: string;
      author_id: string;
      author_name: string;
      author_avatar: string;
      content: string;
      like_count: number;
      create_time: string;
      reply_count: number;
      replies?: Array<{
        comment_id: string;
        author_id: string;
        author_name: string;
        author_avatar: string;
        content: string;
        like_count: number;
        create_time: string;
      }>;
    }) => ({
      commentId: comment.comment_id,
      authorId: comment.author_id,
      authorName: comment.author_name,
      authorAvatar: comment.author_avatar,
      content: comment.content,
      likeCount: comment.like_count,
      commentTime: comment.create_time,
      replyCount: comment.reply_count,
      replies: comment.replies?.map((reply: {
        comment_id: string;
        author_id: string;
        author_name: string;
        author_avatar: string;
        content: string;
        like_count: number;
        create_time: string;
      }) => ({
        commentId: reply.comment_id,
        authorId: reply.author_id,
        authorName: reply.author_name,
        authorAvatar: reply.author_avatar,
        content: reply.content,
        likeCount: reply.like_count,
        commentTime: reply.create_time,
      })),
    }));
  }

  /**
   * 获取评论列表
   */
  async getComments(cookies: string, videoId: string): Promise<CommentData[]> {
    // 优先尝试使用 Python API
    const pythonAvailable = await this.checkPythonServiceAvailable();
    if (pythonAvailable) {
      logger.info('[Douyin] Python service available, using Python API for comments');
      try {
        return await this.getCommentsViaPython(cookies, videoId);
      } catch (pythonError) {
        logger.warn('[Douyin] Python API getComments failed, falling back to Playwright:', pythonError);
      }
    }

    // 回退到 Playwright 方式
    try {
      // 使用无头浏览器后台运行
      await this.initBrowser({ headless: true });
      await this.setCookies(cookies);

      if (!this.page) throw new Error('Page not initialized');

      // 访问评论管理页面
      await this.page.goto(`https://creator.douyin.com/creator-micro/content/comment?video_id=${videoId}`);
      await this.page.waitForLoadState('networkidle');

      // 获取评论列表
      const comments = await this.page.$$eval('.comment-item', items =>
        items.map(item => ({
          commentId: item.getAttribute('data-id') || '',
          authorId: item.querySelector('.author-id')?.textContent?.trim() || '',
          authorName: item.querySelector('.author-name')?.textContent?.trim() || '',
          authorAvatar: item.querySelector('.author-avatar img')?.getAttribute('src') || '',
          content: item.querySelector('.comment-content')?.textContent?.trim() || '',
          likeCount: parseInt(item.querySelector('.like-count')?.textContent || '0'),
          commentTime: item.querySelector('.comment-time')?.textContent?.trim() || '',
        }))
      );

      await this.closeBrowser();

      return comments;
    } catch (error) {
      logger.error('Douyin getComments error:', error);
      await this.closeBrowser();
      return [];
    }
  }

  /**
   * 回复评论
   */
  async replyComment(cookies: string, commentId: string, content: string): Promise<boolean> {
    try {
      // 使用无头浏览器后台运行
      await this.initBrowser({ headless: true });
      await this.setCookies(cookies);

      if (!this.page) throw new Error('Page not initialized');

      // 这里需要实现具体的回复逻辑
      // 由于抖音页面结构可能变化，具体实现需要根据实际情况调整

      logger.info(`Reply to comment ${commentId}: ${content}`);

      await this.closeBrowser();

      return true;
    } catch (error) {
      logger.error('Douyin replyComment error:', error);
      await this.closeBrowser();
      return false;
    }
  }

  /**
   * 获取数据统计
   */
  async getAnalytics(cookies: string, dateRange: DateRange): Promise<AnalyticsData> {
    try {
      // 使用无头浏览器后台运行
      await this.initBrowser({ headless: true });
      await this.setCookies(cookies);

      if (!this.page) throw new Error('Page not initialized');

      // 访问数据中心
      await this.page.goto('https://creator.douyin.com/creator-micro/data/overview');
      await this.page.waitForLoadState('networkidle');

      // 获取数据
      // 这里需要根据实际页面结构获取数据

      await this.closeBrowser();

      return {
        fansCount: 0,
        fansIncrease: 0,
        viewsCount: 0,
        likesCount: 0,
        commentsCount: 0,
        sharesCount: 0,
      };
    } catch (error) {
      logger.error('Douyin getAnalytics error:', error);
      await this.closeBrowser();
      throw error;
    }
  }

  /**
   * 删除已发布的作品
   */
  async deleteWork(
    cookies: string,
    videoId: string,
    onCaptchaRequired?: (captchaInfo: { taskId: string; imageUrl?: string }) => Promise<string>
  ): Promise<{ success: boolean; errorMessage?: string }> {
    try {
      // 使用无头浏览器后台运行
      await this.initBrowser({ headless: true });
      await this.setCookies(cookies);

      if (!this.page) throw new Error('Page not initialized');

      logger.info(`[Douyin Delete] Starting delete for video: ${videoId}`);

      // 访问内容管理页面
      await this.page.goto('https://creator.douyin.com/creator-micro/content/manage', {
        waitUntil: 'networkidle',
        timeout: 60000,
      });

      await this.page.waitForTimeout(3000);

      // 找到对应视频的操作按钮
      // 视频列表通常有 data-aweme-id 属性或者可以通过视频 ID 定位
      const videoCard = this.page.locator(`[data-aweme-id="${videoId}"], [data-video-id="${videoId}"]`).first();

      // 如果没找到，尝试通过其他方式定位
      let found = await videoCard.count() > 0;

      if (!found) {
        // 尝试遍历视频列表找到对应的
        const videoCards = this.page.locator('[class*="video-card"], [class*="content-item"], [class*="aweme-item"]');
        const count = await videoCards.count();

        for (let i = 0; i < count; i++) {
          const card = videoCards.nth(i);
          const html = await card.innerHTML().catch(() => '');
          if (html.includes(videoId)) {
            // 找到对应的视频卡片，点击更多操作
            const moreBtn = card.locator('[class*="more"], [class*="action"]').first();
            if (await moreBtn.count() > 0) {
              await moreBtn.click();
              found = true;
              break;
            }
          }
        }
      }

      if (!found) {
        // 直接访问视频详情页尝试删除
        await this.page.goto(`https://creator.douyin.com/creator-micro/content/manage?aweme_id=${videoId}`, {
          waitUntil: 'networkidle',
        });
        await this.page.waitForTimeout(2000);
      }

      // 查找并点击"更多"按钮或"..."
      const moreSelectors = [
        'button:has-text("更多")',
        '[class*="more-action"]',
        '[class*="dropdown-trigger"]',
        'button[class*="more"]',
        '.semi-dropdown-trigger',
      ];

      for (const selector of moreSelectors) {
        const moreBtn = this.page.locator(selector).first();
        if (await moreBtn.count() > 0) {
          await moreBtn.click();
          await this.page.waitForTimeout(500);
          break;
        }
      }

      // 查找并点击"删除"选项
      const deleteSelectors = [
        'div:has-text("删除"):not(:has(*))',
        '[class*="dropdown-item"]:has-text("删除")',
        'li:has-text("删除")',
        'span:has-text("删除")',
      ];

      for (const selector of deleteSelectors) {
        const deleteBtn = this.page.locator(selector).first();
        if (await deleteBtn.count() > 0) {
          await deleteBtn.click();
          logger.info('[Douyin Delete] Delete button clicked');
          break;
        }
      }

      await this.page.waitForTimeout(1000);

      // 检查是否需要验证码
      const captchaVisible = await this.page.locator('[class*="captcha"], [class*="verify"]').count() > 0;

      if (captchaVisible && onCaptchaRequired) {
        logger.info('[Douyin Delete] Captcha required');

        // 点击发送验证码
        const sendCodeBtn = this.page.locator('button:has-text("发送验证码"), button:has-text("获取验证码")').first();
        if (await sendCodeBtn.count() > 0) {
          await sendCodeBtn.click();
          logger.info('[Douyin Delete] Verification code sent');
        }

        // 通过回调获取验证码
        const taskId = `delete_${videoId}_${Date.now()}`;
        const code = await onCaptchaRequired({ taskId });

        if (code) {
          // 输入验证码
          const codeInput = this.page.locator('input[placeholder*="验证码"], input[type="text"]').first();
          if (await codeInput.count() > 0) {
            await codeInput.fill(code);
            logger.info('[Douyin Delete] Verification code entered');
          }

          // 点击确认按钮
          const confirmBtn = this.page.locator('button:has-text("确定"), button:has-text("确认")').first();
          if (await confirmBtn.count() > 0) {
            await confirmBtn.click();
            await this.page.waitForTimeout(2000);
          }
        }
      }

      // 确认删除（可能有二次确认弹窗）
      const confirmDeleteSelectors = [
        'button:has-text("确认删除")',
        'button:has-text("确定")',
        '.semi-modal-footer button:has-text("确定")',
      ];

      for (const selector of confirmDeleteSelectors) {
        const confirmBtn = this.page.locator(selector).first();
        if (await confirmBtn.count() > 0) {
          await confirmBtn.click();
          await this.page.waitForTimeout(1000);
        }
      }

      logger.info('[Douyin Delete] Delete completed');
      await this.closeBrowser();

      return { success: true };

    } catch (error) {
      logger.error('[Douyin Delete] Error:', error);
      await this.closeBrowser();
      return {
        success: false,
        errorMessage: error instanceof Error ? error.message : '删除失败',
      };
    }
  }

  /**
   * 解析数量字符串
   */
  private parseCount(text: string): number {
    text = text.replace(/,/g, '');

    if (text.includes('万')) {
      return Math.floor(parseFloat(text.replace('万', '')) * 10000);
    }
    if (text.includes('w')) {
      return Math.floor(parseFloat(text.replace('w', '')) * 10000);
    }
    if (text.includes('亿')) {
      return Math.floor(parseFloat(text.replace('亿', '')) * 100000000);
    }

    return parseInt(text) || 0;
  }
}
