"""
小红书登录服务
使用 Playwright 模拟浏览器登录小红书
"""
from playwright.async_api import async_playwright, Browser, Page, BrowserContext
from typing import Dict, Any, Optional
import asyncio
import json
import random
import unicodedata
import sys


class XHSLoginService:
    """小红书登录服务"""
    
    def __init__(self):
        self.playwright = None
        self.browser: Optional[Browser] = None
        self.context: Optional[BrowserContext] = None
        self.page: Optional[Page] = None
        self.current_phone = None
        
    async def init_browser(self, cookies: Optional[list] = None, proxy: Optional[str] = None, user_agent: Optional[str] = None):
        """
        初始化浏览器
        
        Args:
            cookies: 可选的Cookie列表，用于恢复登录状态
            proxy: 可选的代理地址，例如 http://user:pass@ip:port
            user_agent: 可选的自定义User-Agent
        """
        try:
            self.playwright = await async_playwright().start()
            
            # 启动浏览器（使用chromium）
            # headless=True 在服务器环境下运行，不显示浏览器界面
            launch_kwargs = {
                "headless": True,  # 服务器环境使用无头模式，本地调试可改为False
                "args": ['--disable-blink-features=AutomationControlled'],
            }
            if proxy:
                launch_kwargs["proxy"] = {"server": proxy}
            
            self.browser = await self.playwright.chromium.launch(**launch_kwargs)
            
            # 创建浏览器上下文，模拟真实用户
            context_kwargs = {
                "viewport": {'width': 1280, 'height': 720},
                "user_agent": user_agent or 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            }
            self.context = await self.browser.new_context(**context_kwargs)
            
            # 如果提供了Cookies，注入到浏览器上下文
            if cookies:
                await self.context.add_cookies(cookies)
                print(f"已注入 {len(cookies)} 个Cookie", file=sys.stderr)
            
            # 创建新页面
            self.page = await self.context.new_page()
            
            print("浏览器初始化成功", file=sys.stderr)
            
        except Exception as e:
            print(f"浏览器初始化失败: {str(e)}", file=sys.stderr)
            raise
    
    async def close_browser(self):
        """关闭浏览器"""
        try:
            if self.page:
                await self.page.close()
            if self.context:
                await self.context.close()
            if self.browser:
                await self.browser.close()
            if self.playwright:
                await self.playwright.stop()
            print("浏览器已关闭", file=sys.stderr)
        except Exception as e:
            print(f"关闭浏览器异常: {str(e)}", file=sys.stderr)
    
    async def send_verification_code(self, phone: str, country_code: str = "+86") -> Dict[str, Any]:
        """
        发送验证码
        
        Args:
            phone: 手机号
            country_code: 国家区号
            
        Returns:
            Dict containing success status and error message if any
        """
        try:
            if not self.page:
                await self.init_browser()
            
            self.current_phone = phone
            
            # 访问小红书创作者平台登录页（专门的登录页面）
            print(f"正在访问小红书创作者平台登录页...", file=sys.stderr)
            # 直接访问创作者平台登录页面，超时时间延长到60秒
            try:
                await self.page.goto('https://creator.xiaohongshu.com/login', wait_until='domcontentloaded', timeout=60000)
                print("✅ 页面加载完成", file=sys.stderr)
            except Exception as e:
                print(f"访问页面超时，但继续尝试: {str(e)}", file=sys.stderr)
            
            # 等待登录表单加载
            await asyncio.sleep(2)
            print("✅ 已进入创作者平台登录页面", file=sys.stderr)
            
            # 根据记忆：小红书登录跳过协议复选框，无需处理
            # 但保留协议弹窗处理逻辑，以防页面变化
            try:
                await asyncio.sleep(0.5)
                agreement_selectors = [
                    'text="同意并继续"',
                    'text="已阅读并同意"',
                    'button:has-text("同意")',
                    'button:has-text("继续")',
                ]
                
                for selector in agreement_selectors:
                    try:
                        agreement_btn = await self.page.wait_for_selector(selector, timeout=1000)
                        if agreement_btn:
                            await agreement_btn.click()
                            print(f"✅ 已点击协议按钮: {selector}", file=sys.stderr)
                            await asyncio.sleep(0.5)
                            break
                    except Exception:
                        continue
            except Exception as e:
                print(f"无协议弹窗（正常情况）", file=sys.stderr)
            
            # 输入手机号
            try:
                # 创作者平台登录页面的手机号输入框选择器
                print("查找手机号输入框...", file=sys.stderr)
                phone_input_selectors = [
                    'input[placeholder="手机号"]',  # 根据HTML精确匹配
                    'input.css-nt440g',  # 根据HTML中的class
                    'input[placeholder*="手机号"]',
                    'input[autocomplete="on"][autofocus]',
                    'input[type="tel"]',
                    'input[type="text"]',
                    'input[name*="phone"]',
                    'input[name*="mobile"]',
                ]
                
                phone_input = None
                for selector in phone_input_selectors:
                    try:
                        phone_input = await self.page.wait_for_selector(selector, timeout=2000)
                        if phone_input:
                            print(f"✅ 找到手机号输入框: {selector}", file=sys.stderr)
                            break
                    except Exception:
                        continue
                
                if not phone_input:
                    # 打印页面信息用于调试
                    print("⚠️ 未找到手机号输入框，打印页面信息...", file=sys.stderr)
                    print(f"页面URL: {self.page.url}", file=sys.stderr)
                    # 查找所有input元素
                    inputs = await self.page.query_selector_all('input')
                    print(f"页面上找到 {len(inputs)} 个input元素", file=sys.stderr)
                    for i, inp in enumerate(inputs[:5]):
                        try:
                            placeholder = await inp.get_attribute('placeholder')
                            input_type = await inp.get_attribute('type')
                            name = await inp.get_attribute('name')
                            class_name = await inp.get_attribute('class')
                            print(f"Input {i+1}: type={input_type}, placeholder={placeholder}, name={name}, class={class_name}", file=sys.stderr)
                        except Exception:
                            pass
                    
                    return {
                        "success": False,
                        "error": "未找到手机号输入框，请检查页面是否正确加载"
                    }
                
                # 清空并输入手机号
                await phone_input.click()
                await asyncio.sleep(0.2)
                # 使用 Ctrl+A 全选后输入，更快速地清空
                await phone_input.press('Control+A')
                await phone_input.type(phone, delay=50)  # 模拟真实输入，每个字符50ms延迟
                print(f"✅ 已输入手机号: {phone}", file=sys.stderr)
                await asyncio.sleep(0.3)
            except Exception as e:
                return {
                    "success": False,
                    "error": f"输入手机号失败: {str(e)}"
                }
            
            # 点击发送验证码按钮
            try:
                print("查找发送验证码按钮...", file=sys.stderr)
                # 创作者平台登录页面的验证码按钮选择器
                send_code_btn = None
                selectors = [
                    'text="发送验证码"',  # 根据截图
                    'text="重新发送"',  # 根据HTML
                    'div.css-uyobdj',  # 根据HTML中的class
                    'button:has-text("发送验证码")',
                    'button:has-text("重新发送")',
                    'div:has-text("重新发送")',
                    'text="获取验证码"',
                    'button:has-text("获取验证码")',
                ]
                
                for selector in selectors:
                    try:
                        send_code_btn = await self.page.wait_for_selector(selector, timeout=1500)
                        if send_code_btn:
                            print(f"✅ 找到发送验证码按钮: {selector}", file=sys.stderr)
                            break
                    except Exception:
                        continue
                
                if not send_code_btn:
                    # 尝试查找所有按钮和div元素
                    print("⚠️ 未找到预定选择器，查找所有可点击元素...", file=sys.stderr)
                    buttons = await self.page.query_selector_all('button, div[class*="css-"]')
                    print(f"页面上找到 {len(buttons)} 个可能的元素", file=sys.stderr)
                    for i, btn in enumerate(buttons[:20]):  # 查看前20个
                        try:
                            text = await btn.inner_text()
                            if text and len(text.strip()) > 0:  # 只打印有文本的
                                classes = await btn.get_attribute('class')
                                print(f"元素 {i+1}: 文本=[{text.strip()}] class=[{classes}]", file=sys.stderr)
                        except Exception:
                            pass
                    
                    # 尝试根据文本内容查找
                    print("尝试根据文本内容查找验证码按钮...", file=sys.stderr)
                    for btn in buttons:
                        try:
                            text = await btn.inner_text()
                            if text and ('验证码' in text or '发送' in text or '获取' in text or '重新' in text):
                                send_code_btn = btn
                                print(f"✅ 通过文本找到按钮: {text.strip()}", file=sys.stderr)
                                break
                        except Exception:
                            continue
                
                if send_code_btn:
                    await send_code_btn.click()
                    print("✅ 已点击发送验证码", file=sys.stderr)
                    await asyncio.sleep(2)  # 等待验证码发送
                    
                    # 点击后可能再次出现协议弹窗，再次处理
                    try:
                        await asyncio.sleep(0.5)
                        agreement_selectors = [
                            'text="同意并继续"',
                            'text="已阅读并同意"',
                            'button:has-text("同意")',
                        ]
                        
                        for selector in agreement_selectors:
                            try:
                                agreement_btn = await self.page.wait_for_selector(selector, timeout=1000)
                                if agreement_btn:
                                    await agreement_btn.click()
                                    print(f"✅ 再次点击协议按钮: {selector}", file=sys.stderr)
                                    await asyncio.sleep(0.5)
                                    break
                            except Exception:
                                continue
                    except Exception as e:
                        print(f"无二次协议弹窗（正常）", file=sys.stderr)
                else:
                    return {
                        "success": False,
                        "error": "未找到发送验证码按钮，请检查页面结构"
                    }
            except Exception as e:
                return {
                    "success": False,
                    "error": f"点击发送验证码失败: {str(e)}"
                }
            
            # 检查是否需要滑块验证
            try:
                await asyncio.sleep(1)
                # 如果出现滑块，需要手动处理或使用自动化工具
                slider_selectors = [
                    '.slider',
                    '.captcha',
                    '[class*="captcha"]',
                    '[class*="slider"]',
                    '[id*="captcha"]',
                ]
                
                slider_found = False
                for selector in slider_selectors:
                    try:
                        slider = await self.page.query_selector(selector)
                        if slider:
                            slider_found = True
                            print("⚠️ 检测到滑块验证，请手动完成...", file=sys.stderr)
                            # 等待用户手动完成滑块
                            await asyncio.sleep(15)
                            break
                    except Exception:
                        pass
                
                if not slider_found:
                    print("✅ 未检测到滑块验证", file=sys.stderr)
            except Exception as e:
                print(f"滑块检测异常: {str(e)}", file=sys.stderr)
            
            print("\n✅ 验证码发送流程完成，请查看手机短信", file=sys.stderr)
            print("请在小程序中输入收到的验证码并点击登录\n", file=sys.stderr)
            
            return {
                "success": True,
                "message": "验证码发送成功，请查看手机短信"
            }
            
        except Exception as e:
            error_msg = str(e)
            print(f"\n\u274c 发送验证码异常: {error_msg}", file=sys.stderr)
            print(f"当前页面URL: {self.page.url if self.page else 'N/A'}", file=sys.stderr)
            
            # 打印调试信息
            if self.page:
                try:
                    print("尝试截图保存错误状态...", file=sys.stderr)
                    await self.page.screenshot(path='error_screenshot.png')
                    print("✅ 错误状态已截图保存到 error_screenshot.png", file=sys.stderr)
                except Exception:
                    pass
            
            return {
                "success": False,
                "error": error_msg
            }
    
    async def login(self, phone: str, code: str, country_code: str = "+86") -> Dict[str, Any]:
        """
        使用验证码登录
        
        Args:
            phone: 手机号
            code: 验证码
            country_code: 国家区号
            
        Returns:
            Dict containing login result, user info and cookies
        """
        try:
            if not self.page:
                return {
                    "success": False,
                    "error": "页面未初始化，请先发送验证码"
                }
            
            # 输入验证码
            try:
                print("查找验证码输入框...", file=sys.stderr)
                code_input_selectors = [
                    'input[placeholder="验证码"]',  # 根据HTML精确匹配
                    'input.css-1ge5flv',  # 根据HTML中的class
                    'input[placeholder*="验证码"]',
                    'input[type="text"]:not([placeholder*="手机"])',
                ]
                
                code_input = None
                for selector in code_input_selectors:
                    try:
                        code_input = await self.page.wait_for_selector(selector, timeout=2000)
                        if code_input:
                            print(f"✅ 找到验证码输入框: {selector}", file=sys.stderr)
                            break
                    except Exception:
                        continue
                
                if not code_input:
                    return {
                        "success": False,
                        "error": "未找到验证码输入框"
                    }
                
                await code_input.click()
                await asyncio.sleep(0.2)
                await code_input.press('Control+A')
                await code_input.type(code, delay=50)
                print(f"✅ 已输入验证码: {code}", file=sys.stderr)
                await asyncio.sleep(0.5)
            except Exception as e:
                return {
                    "success": False,
                    "error": f"输入验证码失败: {str(e)}"
                }
            
            # 点击登录按钮
            try:
                print("查找登录按钮...", file=sys.stderr)
                login_btn_selectors = [
                    'button.beer-login-btn',  # 根据HTML中的class
                    'button.css-y4h4ay',  # 根据HTML
                    'button:has-text("登 录")',  # 注意有空格
                    'button:has-text("登录")',
                    'text="登 录"',
                    'text="登录"',
                    '.login-button',
                ]
                
                login_btn = None
                for selector in login_btn_selectors:
                    try:
                        login_btn = await self.page.wait_for_selector(selector, timeout=2000)
                        if login_btn:
                            print(f"✅ 找到登录按钮: {selector}", file=sys.stderr)
                            break
                    except Exception:
                        continue
                
                if not login_btn:
                    # 打印所有按钮用于调试
                    print("⚠️ 未找到登录按钮，打印所有按钮...", file=sys.stderr)
                    buttons = await self.page.query_selector_all('button')
                    print(f"页面上找到 {len(buttons)} 个按钮", file=sys.stderr)
                    for i, btn in enumerate(buttons[:10]):
                        try:
                            text = await btn.inner_text()
                            classes = await btn.get_attribute('class')
                            print(f"按钮 {i+1}: 文本=[{text.strip()}] class=[{classes}]", file=sys.stderr)
                        except Exception:
                            pass
                    
                    return {
                        "success": False,
                        "error": "未找到登录按钮"
                    }
                
                await login_btn.click()
                print("✅ 已点击登录按钮", file=sys.stderr)
                
                # 等待一下，检查是否出现协议弹窗
                await asyncio.sleep(1)
                
                # 处理登录后可能出现的协议弹窗
                try:
                    agreement_popup_selectors = [
                        'text="同意并继续"',
                        'button:has-text("同意并继续")',
                        'text="已阅读并同意"',
                    ]
                    
                    for selector in agreement_popup_selectors:
                        try:
                            popup_btn = await self.page.wait_for_selector(selector, timeout=2000)
                            if popup_btn:
                                await popup_btn.click()
                                print(f"✅ 已点击登录后的协议弹窗: {selector}", file=sys.stderr)
                                await asyncio.sleep(1)
                                break
                        except Exception:
                            continue
                except Exception as e:
                    print(f"无登录后协议弹窗（正常）", file=sys.stderr)
                
                # 等待登录完成
                await asyncio.sleep(3)
            except Exception as e:
                return {
                    "success": False,
                    "error": f"点击登录按钮失败: {str(e)}"
                }
            
            # 检查是否登录成功
            try:
                # 等待页面跳转或出现用户信息
                await self.page.wait_for_selector('.user-info, .avatar, [class*="user"]', timeout=10000)
                print("登录成功", file=sys.stderr)
            except Exception as e:
                return {
                    "success": False,
                    "error": f"登录验证失败，可能验证码错误: {str(e)}"
                }
            
            # 获取Cookies
            cookies = await self.context.cookies()
            
            # 注意：这里返回两种格式
            # 1. cookies_dict: 键值对格式（用于 API 返回，方便前端展示）
            # 2. cookies: Playwright 完整格式（用于保存文件和后续使用）
            cookies_dict = {cookie['name']: cookie['value'] for cookie in cookies}
            
            # 打印重要的Cookies
            print(f"\n========== Cookies 信息 ==========", file=sys.stderr)
            print(f"共获取到 {len(cookies)} 个Cookie", file=sys.stderr)
            
            # 打印所有Cookie名称
            print(f"\nCookie名称列表: {list(cookies_dict.keys())}", file=sys.stderr)
            
            # 完整打印所有Cookies（键值对格式）
            print(f"\n完整Cookies内容（键值对格式）:", file=sys.stderr)
            for name, value in cookies_dict.items():
                print(f"  {name}: {value}", file=sys.stderr)
            
            print(f"\n================================\n", file=sys.stderr)
            
            # 获取用户信息（从页面或API）
            user_info = {}
            try:
                # 等待页面完全加载
                await asyncio.sleep(2)
                
                # 尝试从localStorage获取用户信息
                storage = await self.page.evaluate('() => JSON.stringify(localStorage)')
                storage_dict = json.loads(storage)
                
                print(f"LocalStorage内容: {list(storage_dict.keys())}", file=sys.stderr)
                
                # 提取有用的localStorage数据
                useful_keys = ['b1', 'b1b1', 'p1', 'xhs_context_networkQuality']
                for key in useful_keys:
                    if key in storage_dict:
                        try:
                            # 尝试解析JSON
                            value = storage_dict[key]
                            if value and value.strip():
                                user_info[key] = json.loads(value) if value.startswith('{') or value.startswith('[') else value
                        except:
                            user_info[key] = storage_dict[key]
                
                # 小红书可能将用户信息存储在特定键中
                for key, value in storage_dict.items():
                    if 'user' in key.lower():
                        try:
                            user_data = json.loads(value)
                            user_info['user_data'] = user_data
                            print(f"从localStorage获取用户信息 - key: {key}", file=sys.stderr)
                            break
                        except:
                            pass
                
                # 尝试从window对象获取用户信息（更完整的方式）
                try:
                    # 获取window.__INITIAL_STATE__或其他可能的用户信息对象
                    window_data = await self.page.evaluate('''
                        () => {
                            const result = {};
                            
                            // 尝试获取常见的用户信息存储位置
                            if (window.__INITIAL_STATE__) result.initial_state = window.__INITIAL_STATE__;
                            if (window.user) result.user = window.user;
                            if (window.userInfo) result.userInfo = window.userInfo;
                            if (window.__APOLLO_STATE__) result.apollo_state = window.__APOLLO_STATE__;
                            
                            // 尝试从Redux store获取
                            if (window.__REDUX_DEVTOOLS_EXTENSION__) {
                                try {
                                    const state = window.__REDUX_DEVTOOLS_EXTENSION__.extractState();
                                    if (state) result.redux_state = state;
                                } catch(e) {}
                            }
                            
                            return result;
                        }
                    ''')
                    
                    if window_data:
                        user_info['window_data'] = window_data
                        print(f"从window对象获取数据，包含键: {list(window_data.keys())}", file=sys.stderr)
                except Exception as e:
                    print(f"从window对象获取失败: {str(e)}", file=sys.stderr)
                
                # 尝试从页面元素获取用户信息
                if not user_info.get('username'):
                    try:
                        # 尝试获取用户昵称
                        username_el = await self.page.query_selector('.user-name, .username, [class*="user"][class*="name"]')
                        if username_el:
                            username = await username_el.inner_text()
                            user_info['username'] = username
                            print(f"从页面获取用户名: {username}", file=sys.stderr)
                    except:
                        pass
                
                print(f"最终获取到用户信息字段: {list(user_info.keys())}", file=sys.stderr)
                
            except Exception as e:
                print(f"获取用户信息失败: {str(e)}", file=sys.stderr)
            
            # 获取当前URL（可能包含token等信息）
            current_url = self.page.url
            print(f"当前URL: {current_url}", file=sys.stderr)
            
            # 将Cookies保存到文件（Playwright 完整格式）
            try:
                with open('cookies.json', 'w', encoding='utf-8') as f:
                    json.dump(cookies, f, ensure_ascii=False, indent=2)
                print("✅ 已保存 Cookies 到 cookies.json 文件（Playwright 格式）", file=sys.stderr)
                print(f"   文件包含 {len(cookies)} 个完整的 Cookie 对象", file=sys.stderr)
            except Exception as e:
                print(f"保存Cookies文件失败: {str(e)}", file=sys.stderr)
            
            return {
                "success": True,
                "user_info": user_info,
                "cookies": cookies_dict,  # API 返回：键值对格式（方便前端展示）
                "cookies_full": cookies,  # API 返回：完整格式（可选，供需要者使用）
                "url": current_url
            }
            
        except Exception as e:
            print(f"登录异常: {str(e)}", file=sys.stderr)
            return {
                "success": False,
                "error": str(e)
            }
    
    async def get_user_profile(self) -> Dict[str, Any]:
        """
        获取用户详细信息
        登录成功后可以调用此方法获取更多用户信息
        """
        try:
            if not self.page:
                return {
                    "success": False,
                    "error": "页面未初始化"
                }
            
            # 访问用户主页
            await self.page.goto('https://www.xiaohongshu.com/user/profile', wait_until='networkidle')
            await asyncio.sleep(2)
            
            # 这里可以根据实际需求抓取用户信息
            # 示例：获取用户昵称、头像等
            
            return {
                "success": True,
                "profile": {}
            }
            
        except Exception as e:
            return {
                "success": False,
                "error": str(e)
            }
    
    async def verify_login_status(self) -> Dict[str, Any]:
        """
        验证当前登录状态
        访问小红书创作者平台检查是否已登录
        
        Returns:
            Dict containing login status and user info if logged in
        """
        try:
            if not self.page:
                return {
                    "success": False,
                    "logged_in": False,
                    "error": "页面未初始化"
                }
            
            print("正在验证登录状态...", file=sys.stderr)
            
            # 访问小红书创作者平台（而不是首页）
            print("访问创作者平台...", file=sys.stderr)
            try:
                await self.page.goto('https://creator.xiaohongshu.com/', wait_until='domcontentloaded', timeout=60000)
                await asyncio.sleep(2)  # 等待页面加载
                print(f"✅ 已访问创作者平台，当前URL: {self.page.url}", file=sys.stderr)
            except Exception as e:
                print(f"访问创作者平台失败: {str(e)}", file=sys.stderr)
                return {
                    "success": False,
                    "logged_in": False,
                    "error": f"访问创作者平台失败: {str(e)}"
                }
            
            # 检查是否被重定向到登录页（未登录状态）
            current_url = self.page.url
            if 'login' in current_url.lower():
                print("❌ 未登录状态（被重定向到登录页）", file=sys.stderr)
                return {
                    "success": True,
                    "logged_in": False,
                    "cookie_expired": True,  # 标识Cookie已失效
                    "message": "Cookie已失效或未登录",
                    "url": current_url
                }
            
            # 如果在创作者平台主页，说明已登录
            if 'creator.xiaohongshu.com' in current_url and 'login' not in current_url.lower():
                print("✅ 已登录状态（成功访问创作者平台）", file=sys.stderr)
                
                # 获取当前的Cookies
                cookies = await self.context.cookies()
                
                # 转换为键值对格式（用于 API 返回）
                cookies_dict = {cookie['name']: cookie['value'] for cookie in cookies}
                
                # 尝试获取用户信息
                user_info = {}
                try:
                    storage = await self.page.evaluate('() => JSON.stringify(localStorage)')
                    storage_dict = json.loads(storage)
                    
                    # 提取有用的localStorage数据
                    for key, value in storage_dict.items():
                        if 'user' in key.lower():
                            try:
                                user_data = json.loads(value)
                                user_info['user_data'] = user_data
                                break
                            except:
                                pass
                except Exception as e:
                    print(f"获取用户信息失败: {str(e)}", file=sys.stderr)
                
                return {
                    "success": True,
                    "logged_in": True,
                    "message": "Cookie有效，已登录",
                    "cookies": cookies_dict,      # 键值对格式（前端展示）
                    "cookies_full": cookies,       # Playwright完整格式（数据库存储/脚本使用）
                    "user_info": user_info,
                    "url": current_url
                }
            else:
                print("❌ 未登录状态（URL异常）", file=sys.stderr)
                return {
                    "success": True,
                    "logged_in": False,
                    "cookie_expired": True,  # 标识Cookie已失效
                    "message": "Cookie已失效或未登录",
                    "url": current_url
                }
                
        except Exception as e:
            print(f"验证登录状态异常: {str(e)}", file=sys.stderr)
            return {
                "success": False,
                "logged_in": False,
                "error": str(e)
            }
    
    def _calculate_title_width(self, title: str) -> int:
        width = 0
        for ch in title:
            if unicodedata.east_asian_width(ch) in ("F", "W"):
                width += 2
            else:
                width += 1
        return width

    async def publish_note(self, title: str, content: str, images: list = None, topics: list = None, cookies: list = None) -> Dict[str, Any]:
        """
        发布笔记（支持Cookie注入）
        
        Args:
            title: 笔记标题
            content: 笔记内容
            images: 图片路径列表（本地文件路径）
            topics: 话题标签列表
            cookies: 可选的Cookie列表（Playwright完整格式），用于注入登录态
            
        Returns:
            Dict containing publish result
        """
        try:
            # ========== 内容验证 ==========
            print("\n========== 开始验证发布内容 ==========", file=sys.stderr)
            
            # 1. 验证标题长度
            if not title or len(title.strip()) == 0:
                return {
                    "success": False,
                    "error": "标题不能为空",
                    "error_type": "validation_error"
                }

            title = title.strip()
            title_width = self._calculate_title_width(title)
            if title_width > 40:
                return {
                    "success": False,
                    "error": f"标题超出限制：当前宽度 {title_width}，平台限制 40",
                    "error_type": "validation_error"
                }
            print(f"✅ 标题验证通过: 宽度 {title_width}/40", file=sys.stderr)

            # 2. 验证内容长度
            if not content or len(content.strip()) == 0:
                return {
                    "success": False,
                    "error": "内容不能为空",
                    "error_type": "validation_error"
                }
            
            content_length = len(content)
            if content_length > 1000:
                return {
                    "success": False,
                    "error": f"内容超出限制：当前 {content_length} 个字，最多 1000 个字",
                    "error_type": "validation_error"
                }
            print(f"✅ 内容验证通过: {content_length}/1000 个字", file=sys.stderr)
            
            # 3. 验证图片数量
            images_count = len(images) if images else 0
            if images_count == 0:
                return {
                    "success": False,
                    "error": "至少需要 1 张图片",
                    "error_type": "validation_error"
                }
            if images_count > 18:
                return {
                    "success": False,
                    "error": f"图片超出限制：当前 {images_count} 张，最多 18 张",
                    "error_type": "validation_error"
                }
            print(f"✅ 图片数量验证通过: {images_count}/18 张", file=sys.stderr)
            
            print("✅ 所有验证通过，开始发布\n", file=sys.stderr)
            
            # ========== 开始发布流程 ==========
            # 如果提供了Cookie，初始化浏览器并注入Cookie
            if cookies:
                print("✅ 检测到Cookie，将注入到浏览器", file=sys.stderr)
                if not self.page:
                    await self.init_browser(cookies)
                else:
                    # 如果浏览器已存在，添加Cookie
                    await self.context.add_cookies(cookies)
                    print(f"✅ 已注入 {len(cookies)} 个Cookie", file=sys.stderr)
            
            if not self.page:
                return {
                    "success": False,
                    "error": "页面未初始化，请先登录或提供Cookie"
                }
            
            print("\n========== 开始发布笔记 ==========", file=sys.stderr)
            print(f"标题: {title}", file=sys.stderr)
            print(f"内容: {content[:50]}..." if len(content) > 50 else f"内容: {content}", file=sys.stderr)
            print(f"图片数量: {len(images) if images else 0}", file=sys.stderr)
            print(f"话题: {topics if topics else []}", file=sys.stderr)
            
            # 访问官方创作者平台发布页面（带有Cookie的状态下直接访问）
            print("访问创作者平台图文发布页面...", file=sys.stderr)
            try:
                await self.page.goto('https://creator.xiaohongshu.com/publish/publish?source=official', 
                                    wait_until='networkidle', timeout=60000)
                # 等待页面核心元素加载完成，而不是固定时间
                await asyncio.sleep(3)  # 增加等待时间确保JavaScript完全执行

                # 点击「上传图文」tab，符合平台规范
                try:
                    print("查找“上传图文”tab...", file=sys.stderr)
                    tab_selectors = [
                        'button:has-text("上传图文")',
                        'div:has-text("上传图文")',
                        'text="上传图文"',
                    ]
                    tab_clicked = False
                    for selector in tab_selectors:
                        try:
                            tab = await self.page.wait_for_selector(selector, timeout=3000)
                            if tab:
                                await tab.click()
                                tab_clicked = True
                                print(f"✅ 已点击“上传图文”tab: {selector}", file=sys.stderr)
                                await asyncio.sleep(1)
                                break
                        except Exception:
                            continue
                    if not tab_clicked:
                        print("⚠️ 未找到“上传图文”tab，将继续使用当前页面进行发布", file=sys.stderr)
                except Exception as e:
                    print(f"点击“上传图文”tab时异常: {str(e)}", file=sys.stderr)

                print("✅ 已进入图文发布页面", file=sys.stderr)
            except Exception as e:
                return {
                    "success": False,
                    "error": f"访问发布页面失败: {str(e)}"
                }
            
           # 上传图片（如果有）
            if images and len(images) > 0:
                try:
                    print(f"开始上传 {len(images)} 张图片...", file=sys.stderr)
                    await asyncio.sleep(1)  # 等待图文上传页面完全加载
                    
                    # 直接查找图片上传控件（已经在图文上传页面了）
                    print("查找图片上传控件...", file=sys.stderr)
                    upload_selectors = [
                        'input[type="file"][accept*="image"]',
                        'input[type="file"]',
                        'input[accept*="image"]',
                        '.upload-input',
                        '[class*="upload"] input[type="file"]',
                    ]
                    
                    file_input = None
                    for selector in upload_selectors:
                        try:
                            file_input = await self.page.wait_for_selector(selector, timeout=3000)
                            if file_input:
                                print(f"找到文件上传控件: {selector}", file=sys.stderr)
                                break
                        except Exception:
                            continue
                    
                    if file_input:
                        # 批量上传图片
                        images_count = len(images)
                        print(f"正在上传 {images_count} 张图片: {images}", file=sys.stderr)
                        await file_input.set_input_files(images)
                        print(f"已设置文件路径，等待上传...", file=sys.stderr)
                        
                        # 等待所有图片上传完成（检测多张图片）
                        upload_success = False
                        uploaded_count = 0
                        
                        for i in range(20):  # 最多等待20秒（多图需要更长时间）
                            await asyncio.sleep(1)
                            try:
                                # 查找所有已上传的图片缩略图
                                uploaded_images = await self.page.query_selector_all('img[src*="blob:"]')
                                if not uploaded_images:
                                    # 尝试其他选择器
                                    uploaded_images = await self.page.query_selector_all('[class*="image"][class*="item"] img')
                                
                                uploaded_count = len(uploaded_images)
                                
                                if uploaded_count > 0:
                                    print(f"✅ 已上传 {uploaded_count}/{images_count} 张图片", file=sys.stderr)
                                    
                                    # 检查是否所有图片都已上传
                                    if uploaded_count >= images_count:
                                        print(f"✅ 所有图片上传完成！共 {uploaded_count} 张", file=sys.stderr)
                                        upload_success = True
                                        break
                                
                                print(f"等待图片上传... {uploaded_count}/{images_count} ({i+1}/20秒)", file=sys.stderr)
                            except Exception as e:
                                print(f"检测上传状态异常: {e}", file=sys.stderr)
                                pass
                        
                        if upload_success:
                            print(f"✅ 图片上传成功！共 {uploaded_count} 张", file=sys.stderr)
                            await asyncio.sleep(2)  # 额外等待2秒确保完全上传
                        else:
                            print(f"⚠️ 仅检测到 {uploaded_count}/{images_count} 张图片，但继续执行...", file=sys.stderr)
                    else:
                        print("未找到隐藏的file input，尝试查找可点击的上传区域...", file=sys.stderr)
                        
                        # 调试: 打印页面上所有包含upload的元素
                        try:
                            all_elements = await self.page.query_selector_all('[class*="upload"], [id*="upload"]')
                            print(f"\u627e到 {len(all_elements)} 个包含upload的元素", file=sys.stderr)
                            for i, el in enumerate(all_elements[:10]):  # 只看前10个
                                try:
                                    tag_name = await el.evaluate('el => el.tagName')
                                    class_name = await el.evaluate('el => el.className')
                                    print(f"  [{i+1}] {tag_name} class='{class_name}'", file=sys.stderr)
                                except Exception:
                                    pass
                        except Exception:
                            pass
                        
                        # 尝试点击上传区域或按钮
                        upload_area_selectors = [
                            '[class*="upload"][class*="box"]',
                            '[class*="upload"][class*="area"]',
                            '[class*="upload"][class*="wrapper"]',
                            '.upload-zone',
                            'div:has-text("上传图片")',
                            'div:has-text("点击上传")',
                            'button:has-text("上传图片")',
                        ]
                        
                        clicked = False
                        for selector in upload_area_selectors:
                            try:
                                area = await self.page.wait_for_selector(selector, timeout=2000)
                                if area:
                                    print(f"找到上传区域: {selector}", file=sys.stderr)
                                    await area.click()
                                    await asyncio.sleep(0.5)
                                    # 点击后再次查找file input
                                    file_input = await self.page.wait_for_selector('input[type="file"]', timeout=2000)
                                    if file_input:
                                        images_count = len(images)
                                        print(f"正在上传 {images_count} 张图片: {images}", file=sys.stderr)
                                        await file_input.set_input_files(images)
                                        print(f"已设置文件路径，等待上传...", file=sys.stderr)
                                        
                                        # 等待所有图片上传完成
                                        upload_success = False
                                        uploaded_count = 0
                                        
                                        for i in range(20):
                                            await asyncio.sleep(1)
                                            try:
                                                uploaded_images = await self.page.query_selector_all('img[src*="blob:"]')
                                                if not uploaded_images:
                                                    uploaded_images = await self.page.query_selector_all('[class*="image"][class*="item"] img')
                                                
                                                uploaded_count = len(uploaded_images)
                                                
                                                if uploaded_count > 0:
                                                    print(f"✅ 已上传 {uploaded_count}/{images_count} 张图片", file=sys.stderr)
                                                    
                                                    if uploaded_count >= images_count:
                                                        print(f"✅ 所有图片上传完成！共 {uploaded_count} 张", file=sys.stderr)
                                                        upload_success = True
                                                        break
                                                
                                                print(f"等待图片上传... {uploaded_count}/{images_count} ({i+1}/20秒)", file=sys.stderr)
                                            except Exception as e:
                                                print(f"检测上传状态异常: {e}", file=sys.stderr)
                                                pass
                                        
                                        if upload_success:
                                            print(f"✅ 图片上传成功！共 {uploaded_count} 张", file=sys.stderr)
                                            await asyncio.sleep(2)
                                        else:
                                            print(f"⚠️ 仅检测到 {uploaded_count}/{images_count} 张图片，但继续执行...", file=sys.stderr)
                                        
                                        clicked = True
                                    break
                            except Exception:
                                continue
                        
                        if not clicked:
                            print("⚠️ 未找到任何上传控件，跳过图片上传", file=sys.stderr)
                        
                except Exception as e:
                    print(f"上传图片失败: {str(e)}", file=sys.stderr)
                    # 不中断流程，继续发布文字
            
            # 输入标题和内容
            try:
                print("开始输入文字内容...", file=sys.stderr)
                
                # 查找标题输入框(使用显式等待确保元素可交互)
                title_selectors = [
                    'input[placeholder*="标题"]',
                    'input[placeholder*="填写标题"]',
                    'input[placeholder*="曝光"]',
                    '.title-input',
                    '[class*="title"] input',
                ]
                
                title_input = None
                for selector in title_selectors:
                    try:
                        # 等待元素可见且可编辑
                        title_input = await self.page.wait_for_selector(
                            selector, 
                            state='visible',  # 确保元素可见
                            timeout=5000  # 增加超时时间
                        )
                        if title_input:
                            # 确保元素可交互(等待一小段时间让JS初始化完成)
                            await asyncio.sleep(0.5)
                            print(f"找到标题输入框: {selector}", file=sys.stderr)
                            break
                    except Exception as e:
                        print(f"选择器 {selector} 未找到: {str(e)}", file=sys.stderr)
                        continue
                
                if title_input:
                    await title_input.click()
                    await asyncio.sleep(0.3)
                    await title_input.fill(title)
                    print(f"已输入标题: {title}", file=sys.stderr)
                else:
                    print("未找到标题输入框，可能不需要单独标题", file=sys.stderr)
                
                # 查找内容输入框(正文)(使用显式等待确保元素可交互)
                content_selectors = [
                    'div[contenteditable="true"]',
                    'div[placeholder*="正文"]',
                    'div[placeholder*="输入正文"]',
                    'textarea[placeholder*="输入正文"]',
                    'textarea[placeholder*="填写笔记内容"]',
                    'textarea[placeholder*="笔记内容"]',
                    '[class*="content"] div[contenteditable="true"]',
                    '[class*="editor"] div[contenteditable="true"]',
                    'textarea',
                ]
                
                content_input = None
                for selector in content_selectors:
                    try:
                        # 等待元素可见且可编辑
                        content_input = await self.page.wait_for_selector(
                            selector,
                            state='visible',  # 确保元素可见
                            timeout=5000  # 增加超时时间
                        )
                        if content_input:
                            # 确保元素可交互
                            await asyncio.sleep(0.5)
                            print(f"找到内容输入框: {selector}", file=sys.stderr)
                            break
                    except Exception as e:
                        print(f"选择器 {selector} 未找到: {str(e)}", file=sys.stderr)
                        continue
                
                if content_input:
                    # 清空并输入内容
                    await content_input.click()
                    await asyncio.sleep(0.5)
                    
                    # 检查是否是contenteditable元素
                    try:
                        is_contenteditable = await content_input.evaluate('el => el.getAttribute("contenteditable") === "true"')
                        if is_contenteditable:
                            # 使用innerText设置内容
                            await content_input.evaluate(f'el => el.innerText = {json.dumps(content)}')
                        else:
                            # 普通textarea
                            await content_input.fill(content)
                    except Exception:
                        # 如果判断失败，尝试直接fill
                        await content_input.fill(content)
                    
                    print("已输入笔记内容", file=sys.stderr)
                    await asyncio.sleep(0.5)
                    
                    # 添加话题标签
                    if topics:
                        print(f"添加话题标签: {topics}", file=sys.stderr)
                        for topic in topics:
                            # 在内容末尾添加话题
                            topic_text = f" #{topic}"
                            try:
                                is_contenteditable = await content_input.evaluate('el => el.getAttribute("contenteditable") === "true"')
                                if is_contenteditable:
                                    await content_input.evaluate(f'el => el.innerText += {json.dumps(topic_text)}')
                                else:
                                    current_value = await content_input.evaluate('el => el.value')
                                    await content_input.fill(current_value + topic_text)
                            except Exception:
                                # 如果添加失败，继续下一个
                                pass
                        print(f"已添加 {len(topics)} 个话题标签", file=sys.stderr)
                    
                    await asyncio.sleep(1)

                    # 单独在话题输入框中模拟人类方式输入标签
                    if topics:
                        print("尝试在话题输入框中逐个输入标签...", file=sys.stderr)
                        tag_input_selectors = [
                            'input[placeholder*="话题"]',
                            'input[placeholder*="#"]',
                            'input[placeholder*="添加标签"]',
                            '[class*="tag"] input',
                            '[class*="topic"] input',
                        ]
                        tag_input = None
                        for selector in tag_input_selectors:
                            try:
                                tag_input = await self.page.wait_for_selector(selector, timeout=3000)
                                if tag_input:
                                    print(f"找到话题输入框: {selector}", file=sys.stderr)
                                    break
                            except Exception:
                                continue
                        if tag_input:
                            for topic in topics:
                                try:
                                    await tag_input.click()
                                    await asyncio.sleep(0.3)
                                    # 清空已有内容
                                    try:
                                        await tag_input.fill("")
                                    except Exception:
                                        pass
                                    await tag_input.type("#" + topic, delay=50)
                                    await asyncio.sleep(0.8)
                                    # 等待联想列表并选择第一项
                                    suggestion = None
                                    suggestion_selectors = [
                                        '[class*="suggest"] li',
                                        '[role="listbox"] li',
                                        '[class*="dropdown"] li',
                                    ]
                                    for s_selector in suggestion_selectors:
                                        try:
                                            suggestion = await self.page.query_selector(s_selector)
                                            if suggestion:
                                                break
                                        except Exception:
                                            continue
                                    if suggestion:
                                        await suggestion.click()
                                        print(f"✅ 已选择联想话题: {topic}", file=sys.stderr)
                                    else:
                                        # 没有联想列表时，通过回车确认
                                        await tag_input.press("Enter")
                                        print(f"✅ 未找到联想列表，使用回车确认话题: {topic}", file=sys.stderr)
                                    await asyncio.sleep(0.5)
                                except Exception as e:
                                    print(f"添加话题 {topic} 到输入框失败: {str(e)}", file=sys.stderr)
                        else:
                            print("⚠️ 未找到话题输入框，已退回到在正文中追加 #话题 的方式", file=sys.stderr)
                else:
                    return {
                        "success": False,
                        "error": "未找到内容输入框"
                    }
                    
            except Exception as e:
                return {
                    "success": False,
                    "error": f"输入内容失败: {str(e)}"
                }
            
            # 模拟简单的人类滚动行为
            try:
                for _ in range(3):
                    await self.page.mouse.wheel(0, random.randint(200, 500))
                    await asyncio.sleep(random.uniform(0.3, 0.8))
            except Exception:
                pass

            # 点击发布按钮
            try:
                print("查找发布按钮...", file=sys.stderr)
                submit_selectors = [
                    'button:has-text("发布笔记")',
                    'button:has-text("发布")',
                    'text="发布笔记"',
                    'text="发布"',
                    '.publish-btn',
                    '.submit-btn',
                ]
                
                submit_btn = None
                for selector in submit_selectors:
                    try:
                        submit_btn = await self.page.wait_for_selector(selector, timeout=3000)
                        if submit_btn:
                            # 检查按钮是否可点击
                            is_disabled = await submit_btn.evaluate('el => el.disabled')
                            if not is_disabled:
                                print(f"找到发布按钮: {selector}", file=sys.stderr)
                                break
                            else:
                                submit_btn = None
                    except Exception:
                        continue
                
                if submit_btn:
                    # 设置网络监听,捕获发布接口响应
                    note_id = None
                    share_link = None
                                
                    async def handle_response(response):
                        nonlocal note_id, share_link
                        try:
                            # 监听发布笔记的API响应
                            if '/web_api/sns/v2/note' in response.url:
                                print(f"✅ 捕获到发布API响应: {response.url}", file=sys.stderr)
                                if response.status == 200:
                                    try:
                                        data = await response.json()
                                        print(f"API响应数据: {json.dumps(data, ensure_ascii=False)}", file=sys.stderr)
                                                    
                                        if data.get('success') and data.get('data'):
                                            note_id = data['data'].get('id')
                                            # 优先使用share_link,如果没有则使用note_id拼接
                                            if 'share_link' in data:
                                                share_link = data['share_link']
                                                print(f"✅ 获取到笔记链接: {share_link}", file=sys.stderr)
                                            elif note_id:
                                                share_link = f"https://www.xiaohongshu.com/discovery/item/{note_id}"
                                                print(f"✅ 根据ID生成笔记链接: {share_link}", file=sys.stderr)
                                    except Exception as e:
                                        print(f"解析API响应失败: {str(e)}", file=sys.stderr)
                        except Exception as e:
                            print(f"处理响应失败: {str(e)}", file=sys.stderr)
                                
                    # 添加响应监听器
                    self.page.on('response', handle_response)
                                
                    await submit_btn.click()
                    print("✅ 已点击发布按钮", file=sys.stderr)
                    await asyncio.sleep(3)  # 等待更长时间以捕获API响应
                    
                    # 检查是否出现社区规范限制提示
                    print("检查是否有社区规范限制...", file=sys.stderr)
                    try:
                        # 尝试查找各种可能的错误提示
                        error_selectors = [
                            'text="因违反社区规范禁止发笔记"',
                            'text*="违反社区规范"',
                            'text*="禁止发布"',
                            'text*="账号被限制"',
                            'text*="账号异常"',
                            '.error-tip',
                            '.warning-tip',
                            '[class*="error"]',
                            '[class*="warning"]',
                        ]
                        
                        for selector in error_selectors:
                            try:
                                error_el = await self.page.wait_for_selector(selector, timeout=2000)
                                if error_el:
                                    error_text = await error_el.inner_text()
                                    print(f"❌ 检测到错误提示: {error_text}", file=sys.stderr)
                                    return {
                                        "success": False,
                                        "error": f"发布失败: {error_text}",
                                        "error_type": "community_violation",  # 标记错误类型
                                        "message": error_text
                                    }
                            except Exception:
                                continue
                    except Exception as e:
                        print(f"检查错误提示异常: {str(e)}", file=sys.stderr)
                    
                    # 检查是否发布成功
                    print("检查发布结果...", file=sys.stderr)
                    try:
                        await asyncio.sleep(2)  # 等待发布完成
                        
                        # 如果捕获到了真实的笔记链接,直接返回
                        if share_link:
                            print(f"✅ 发布成功,获取到笔记链接: {share_link}", file=sys.stderr)
                            return {
                                "success": True,
                                "message": "笔记发布成功",
                                "data": {
                                    "note_id": note_id,
                                    "note_url": share_link
                                },
                                "url": share_link  # 保持兼容性
                            }
                        
                        # 如果没有捕获到,使用原来的逻辑
                        # 等待发布成功的提示或页面跳转
                        success_selectors = [
                            'text="发布成功"',
                            'text="发布完成"',
                            'text*="成功"',
                            '.success-tip',
                            '.success-message',
                        ]
                        
                        publish_success = False
                        for selector in success_selectors:
                            try:
                                success_el = await self.page.wait_for_selector(selector, timeout=3000)
                                if success_el:
                                    success_text = await success_el.inner_text()
                                    print(f"✅ 检测到发布成功提示: {success_text}", file=sys.stderr)
                                    publish_success = True
                                    break
                            except Exception:
                                continue
                        
                        # 如果没有明确的成功提示，检查URL是否变化
                        current_url = self.page.url
                        if not publish_success:
                            # 如果还在发布页面，可能是发布失败
                            if 'publish' in current_url.lower():
                                print("⚠️ 未检测到成功提示，但继续执行", file=sys.stderr)
                            else:
                                print("✅ URL已变化，似乎发布成功", file=sys.stderr)
                                publish_success = True
                        
                        print(f"发布后URL: {current_url}", file=sys.stderr)
                        
                        return {
                            "success": True,
                            "message": "笔记发布成功",
                            "url": current_url
                        }
                    except Exception as e:
                        print(f"检查发布结果异常: {str(e)}", file=sys.stderr)
                        # 即使检查异常，也返回成功（因为按钮已点击）
                        return {
                            "success": True,
                            "message": "笔记已提交发布，但未能确认结果",
                            "url": self.page.url
                        }
                else:
                    return {
                        "success": False,
                        "error": "未找到可用的发布按钮，可能内容不完整"
                    }
                    
            except Exception as e:
                return {
                    "success": False,
                    "error": f"点击发布按钮失败: {str(e)}"
                }
                
        except Exception as e:
            print(f"发布笔记异常: {str(e)}", file=sys.stderr)
            return {
                "success": False,
                "error": str(e)
            }
