# -*- coding: utf-8 -*-
"""
小红书发布工具 - 浏览器自动化模块
"""
import asyncio
import os
import sys
import io
from pathlib import Path
from typing import List, Optional

# 修复 Windows 控制台编码
if sys.platform == "win32":
    try:
        if sys.stdout and hasattr(sys.stdout, 'buffer') and sys.stdout.buffer:
            sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
        if sys.stderr and hasattr(sys.stderr, 'buffer') and sys.stderr.buffer:
            sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
    except Exception:
        pass

try:
    from playwright.async_api import async_playwright, Browser, Page
except ImportError:
    print("❌ 未安装 playwright，请运行：pip install playwright && playwright install chromium")
    sys.exit(1)


class XiaohongshuPublisher:
    """小红书发布器"""

    # 小红书创作者中心地址
    CREATOR_URL = "https://creator.xiaohongshu.com/publish/publish"

    # 用户数据目录（保存登录状态）
    USER_DATA_DIR = Path(__file__).parent / "browser_data"

    def __init__(self, headless: bool = False):
        """
        初始化发布器

        Args:
            headless: 是否无头模式（建议 False，方便登录和确认）
        """
        self.headless = headless
        self.browser: Optional[Browser] = None
        self.page: Optional[Page] = None
        self.context = None
        self.playwright = None
        self.last_stage = "idle"
        self.last_error: Optional[str] = None

    def _set_stage(self, stage: str):
        self.last_stage = stage

    def _fail(self, stage: str, message: str) -> bool:
        self.last_stage = stage
        self.last_error = message
        print(f"❌ [{stage}] {message}")
        return False
    
    async def start(self):
        """启动浏览器"""
        print("🚀 启动浏览器...")
        
        # 确保用户数据目录存在
        self.USER_DATA_DIR.mkdir(parents=True, exist_ok=True)
        
        self.playwright = await async_playwright().start()
        
        # 尝试使用系统浏览器
        browser_paths = [
            r"C:\Program Files\Google\Chrome\Application\chrome.exe",
            r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
            r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
            r"C:\Program Files\Microsoft\Edge\Application\msedge.exe",
        ]
        
        executable_path = None
        for path in browser_paths:
            if os.path.exists(path):
                executable_path = path
                print(f"📍 使用浏览器: {path}")
                break
        
        try:
            # 使用持久化上下文保存登录状态
            if executable_path:
                self.context = await self.playwright.chromium.launch_persistent_context(
                    executable_path=executable_path,
                    user_data_dir=str(self.USER_DATA_DIR),
                    headless=self.headless,
                    viewport={"width": 1280, "height": 800},
                    locale="zh-CN"
                )
            else:
                self.context = await self.playwright.chromium.launch_persistent_context(
                    user_data_dir=str(self.USER_DATA_DIR),
                    headless=self.headless,
                    viewport={"width": 1280, "height": 800},
                    locale="zh-CN"
                )
        except Exception as e:
            print(f"⚠️ 启动失败，尝试下载浏览器: {e}")
            # 如果系统浏览器失败，尝试下载
            import subprocess
            subprocess.run(["playwright", "install", "chromium"], check=True)
            self.context = await self.playwright.chromium.launch_persistent_context(
                user_data_dir=str(self.USER_DATA_DIR),
                headless=self.headless,
                viewport={"width": 1280, "height": 800},
                locale="zh-CN"
            )
        
        self.page = await self.context.new_page()
        print("✅ 浏览器启动成功")
    
    async def close(self):
        """关闭浏览器"""
        if self.context:
            await self.context.close()
        if self.playwright:
            await self.playwright.stop()
        print("👋 浏览器已关闭")
    
    async def check_login(self) -> bool:
        """检查是否已登录"""
        self._set_stage("check-login")
        print("🔍 检查登录状态...")

        await self.page.goto(self.CREATOR_URL, wait_until="networkidle")
        await asyncio.sleep(2)

        current_url = self.page.url
        if "login" in current_url or "passport" in current_url:
            return self._fail("check-login", f"未登录小红书创作者中心，当前页面: {current_url}")

        print("✅ 已登录")
        return True
    
    async def wait_for_login(self, timeout: int = 120):
        """等待用户登录"""
        self._set_stage("wait-login")
        print(f"⏳ 请在浏览器中登录，最多等待 {timeout} 秒...")

        for i in range(timeout):
            await asyncio.sleep(1)
            current_url = self.page.url
            if "login" not in current_url and "passport" not in current_url:
                print("✅ 登录成功！")
                return True
            if i % 10 == 0 and i > 0:
                print(f"   等待中... ({i}/{timeout}s)")

        return self._fail("wait-login", f"登录超时，停留页面: {self.page.url}")
    
    async def publish_note(
        self,
        title: str,
        content: str,
        tags: List[str] = None,
        images: List[str] = None
    ) -> bool:
        """
        发布笔记

        Args:
            title: 标题
            content: 正文
            tags: 标签列表
            images: 图片路径列表

        Returns:
            是否成功填充内容
        """
        try:
            self._set_stage("open-publish-page")
            print("📄 打开发布页面...")
            await self.page.goto(self.CREATOR_URL, wait_until="networkidle")
            await asyncio.sleep(2)

            current_url = self.page.url
            if "login" in current_url or "passport" in current_url:
                return self._fail("open-publish-page", f"打开发布页后跳转到了登录页: {current_url}")

            normalized_title = (title or "").strip()
            normalized_content = (content or "").strip()
            if not normalized_content:
                return self._fail("prepare-content", "缺少正文内容，未执行自动填充")
            if not normalized_title:
                normalized_title = normalized_content[:20] + ("..." if len(normalized_content) > 20 else "")
                print(f"ℹ️ 未提供标题，自动使用正文前缀作为标题: {normalized_title}")

            if images:
                self._set_stage("upload-images")
                print(f"📷 上传 {len(images)} 张图片...")
                upload_btn = await self.page.query_selector('input[type="file"]')
                if not upload_btn:
                    return self._fail("upload-images", "未找到图片上传控件")
                for img_path in images:
                    if not os.path.exists(img_path):
                        return self._fail("upload-images", f"图片不存在: {img_path}")
                    await upload_btn.set_input_files(img_path)
                    await asyncio.sleep(1)

            await asyncio.sleep(2)

            self._set_stage("fill-title")
            print("✏️ 填充标题...")
            title_input = await self.page.query_selector('input[placeholder*="标题"]')
            if not title_input:
                title_input = await self.page.query_selector('.title-input input')
            if not title_input:
                return self._fail("fill-title", "未找到标题输入框")
            await title_input.fill(normalized_title)

            self._set_stage("fill-content")
            print("✏️ 填充正文...")
            content_selectors = [
                '#post-textarea',
                'textarea[placeholder*="正文"]',
                '.content-input textarea',
                '.ql-editor'
            ]

            content_area = None
            selected_selector = None
            for selector in content_selectors:
                candidate = await self.page.query_selector(selector)
                if candidate:
                    content_area = candidate
                    selected_selector = selector
                    break

            if not content_area:
                return self._fail("fill-content", "未找到正文输入框")

            await content_area.fill(normalized_content)
            print(f"ℹ️ 命中正文选择器: {selected_selector}")

            if tags:
                self._set_stage("append-tags")
                print(f"🏷️ 添加 {len(tags)} 个标签...")
                tags_text = " ".join([f"#{tag}" if not tag.startswith("#") else tag for tag in tags])
                current_content = await content_area.input_value()
                await content_area.fill(f"{current_content}\n\n{tags_text}")

            self._set_stage("ready-for-manual-publish")
            print("\n" + "="*50)
            print("✅ 内容填充完成！")
            print("="*50)
            print("📌 当前脚本只负责打开页面并自动填充内容，不会自动点击最终「发布」按钮")
            print("💡 请在浏览器中继续确认：")
            print("   - 检查标题是否正确")
            print("   - 检查正文格式是否正常")
            print("   - 确认图片已上传")
            print("   - 添加合适的话题标签")
            print("   - 手动点击最终发布")
            print("="*50 + "\n")

            return True

        except Exception as e:
            return self._fail(self.last_stage or "publish-note", f"发布流程异常: {e}")
    
    async def interactive_publish(
        self,
        title: str = None,
        content: str = None,
        tags: List[str] = None,
        images: List[str] = None
    ):
        """
        交互式发布
        
        如果没有提供参数，会提示用户输入
        """
        await self.start()
        
        try:
            # 检查登录
            if not await self.check_login():
                if not await self.wait_for_login():
                    return False
            
            # 如果没有提供内容，提示输入
            if not content:
                print("\n📝 请输入笔记内容（输入空行结束）：")
                lines = []
                while True:
                    line = input()
                    if not line:
                        break
                    lines.append(line)
                content = "\n".join(lines)
            
            if not title:
                title = content[:20] + "..." if len(content) > 20 else content
            
            # 发布
            await self.publish_note(title, content, tags, images)
            
            # 保持浏览器打开，让用户确认发布
            print("🔧 浏览器保持打开，发布后关闭此窗口即可...")
            input("按回车键关闭浏览器...")
            
        finally:
            await self.close()


async def main():
    """主函数"""
    import argparse
    
    parser = argparse.ArgumentParser(description="小红书发布工具")
    parser.add_argument("--title", "-t", help="笔记标题")
    parser.add_argument("--content", "-c", help="笔记正文")
    parser.add_argument("--tags", help="标签（逗号分隔）")
    parser.add_argument("--images", "-i", help="图片路径（逗号分隔）")
    parser.add_argument("--headless", action="store_true", help="无头模式")
    
    args = parser.parse_args()
    
    # 解析参数
    tags = [t.strip() for t in args.tags.split(",")] if args.tags else None
    images = [i.strip() for i in args.images.split(",")] if args.images else None
    
    # 发布
    publisher = XiaohongshuPublisher(headless=args.headless)
    await publisher.interactive_publish(
        title=args.title,
        content=args.content,
        tags=tags,
        images=images
    )


if __name__ == "__main__":
    asyncio.run(main())
