#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
智媒通视频发布服务 - 统一入口
支持平台: 抖音、小红书、视频号、快手

参考项目: matrix (https://github.com/kebenxiaoming/matrix)

使用方式:
    python app.py                    # 启动 HTTP 服务 (端口 5005)
    python app.py --port 8080        # 指定端口
    python app.py --headless false   # 显示浏览器窗口
"""

import asyncio
import os
import sys
import argparse
import random
import re
import time

# 禁用输出缓冲，确保 print 立即输出
os.environ['PYTHONUNBUFFERED'] = '1'

# 修复 Windows 终端中文输出乱码
if sys.platform == 'win32':
    import io
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace', line_buffering=True)
    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace', line_buffering=True)
    # 设置环境变量
    os.environ['PYTHONIOENCODING'] = 'utf-8'
import traceback
import requests
from datetime import datetime, date
from pathlib import Path

# 确保当前目录在 Python 路径中
CURRENT_DIR = Path(__file__).parent.resolve()
if str(CURRENT_DIR) not in sys.path:
    sys.path.insert(0, str(CURRENT_DIR))

# 从 server/.env 文件加载环境变量
def load_env_file():
    """从 server/.env 文件加载环境变量"""
    env_path = CURRENT_DIR.parent / '.env'
    if env_path.exists():
        print(f"[Config] Loading env from: {env_path}")
        with open(env_path, 'r', encoding='utf-8') as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith('#') and '=' in line:
                    key, value = line.split('=', 1)
                    key = key.strip()
                    value = value.strip()
                    # 移除引号
                    if value.startswith('"') and value.endswith('"'):
                        value = value[1:-1]
                    elif value.startswith("'") and value.endswith("'"):
                        value = value[1:-1]
                    # 只在环境变量未设置时加载
                    if key not in os.environ:
                        os.environ[key] = value
                        safe_key = key.upper()
                        is_sensitive = any(p in safe_key for p in ['PASSWORD', 'SECRET', 'TOKEN', 'KEY', 'ENCRYPT'])
                        print(f"[Config] Loaded: {key}=***" if is_sensitive else f"[Config] Loaded: {key}={value}")
    else:
        print(f"[Config] .env file not found: {env_path}")

# 加载环境变量
load_env_file()

from flask import Flask, request, jsonify
from flask_cors import CORS

from platforms import get_publisher, PLATFORM_MAP
from platforms.base import PublishParams
from platforms.weixin import WeixinPublisher

def parse_datetime(date_str: str):
    """解析日期时间字符串"""
    if not date_str:
        return None
    
    formats = [
        "%Y-%m-%d %H:%M:%S",
        "%Y-%m-%d %H:%M",
        "%Y/%m/%d %H:%M:%S",
        "%Y/%m/%d %H:%M",
        "%Y-%m-%dT%H:%M:%S",
        "%Y-%m-%dT%H:%M:%SZ",
    ]
    
    for fmt in formats:
        try:
            return datetime.strptime(date_str, fmt)
        except ValueError:
            continue
    
    return None


def _extract_ip_ports(text: str):
    if not text:
        return []
    matches = re.findall(r'\b(?:\d{1,3}\.){3}\d{1,3}:\d{2,5}\b', text)
    seen = set()
    results = []
    for m in matches:
        if m in seen:
            continue
        seen.add(m)
        results.append(m)
    return results


def _mask_ip_port(ip_port: str) -> str:
    try:
        host, port = ip_port.split(':', 1)
        parts = host.split('.')
        if len(parts) == 4:
            return f"{parts[0]}.{parts[1]}.{parts[2]}.***:{port}"
    except Exception:
        pass
    return '***'


def _build_requests_proxy_meta(host: str, port: int, username: str = '', password: str = '') -> str:
    host = str(host).strip()
    port = int(port)
    if username and password:
        return f"http://{username}:{password}@{host}:{port}"
    return f"http://{host}:{port}"


def _test_proxy_connectivity(test_url: str, host: str, port: int, username: str = '', password: str = '', timeout: int = 10) -> bool:
    proxy_meta = _build_requests_proxy_meta(host, port, username, password)
    proxies = {"http": proxy_meta, "https": proxy_meta}
    start = int(round(time.time() * 1000))
    try:
        session = requests.Session()
        session.trust_env = False
        resp = session.get(test_url, proxies=proxies, timeout=timeout)
        _ = resp.text
        cost = int(round(time.time() * 1000)) - start
        print(f"[Proxy] test ok: {_mask_ip_port(host + ':' + str(port))} cost={cost}ms", flush=True)
        return True
    except Exception as e:
        print(f"[Proxy] test failed: {_mask_ip_port(host + ':' + str(port))} err={type(e).__name__}", flush=True)
        return False


def _test_proxy_for_platform(host: str, port: int, platform: str, timeout: int = 15) -> dict:
    """
    测试代理IP对特定平台的可用性
    
    Returns:
        dict: {"ok": bool, "blocked": bool, "cost_ms": int, "error": str}
    """
    # 不同平台的测试URL
    platform_test_urls = {
        "douyin": "https://creator.douyin.com/",
        "xiaohongshu": "https://creator.xiaohongshu.com/",
        "kuaishou": "https://cp.kuaishou.com/",
        "weixin": "https://channels.weixin.qq.com/",
    }
    
    test_url = platform_test_urls.get(platform, "https://www.baidu.com/")
    proxy_meta = _build_requests_proxy_meta(host, port)
    proxies = {"http": proxy_meta, "https": proxy_meta}
    
    start = int(round(time.time() * 1000))
    
    try:
        session = requests.Session()
        session.trust_env = False
        session.headers.update({
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
            "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
        })
        
        resp = session.get(test_url, proxies=proxies, timeout=timeout, allow_redirects=True)
        cost_ms = int(round(time.time() * 1000)) - start
        
        # 检查是否被重定向到验证页面
        final_url = resp.url.lower()
        blocked_indicators = ["captcha", "verify", "challenge", "blocked", "forbidden", "error", "login"]
        is_blocked = any(indicator in final_url for indicator in blocked_indicators)
        
        # 检查响应状态码
        if resp.status_code == 403 or resp.status_code == 429:
            is_blocked = True
        
        result = {
            "ok": not is_blocked and resp.status_code < 400,
            "blocked": is_blocked,
            "cost_ms": cost_ms,
            "status_code": resp.status_code,
            "final_url": resp.url,
        }
        
        if is_blocked:
            print(f"[Proxy] platform {platform} blocked: {_mask_ip_port(host + ':' + str(port))} status={resp.status_code}", flush=True)
        else:
            print(f"[Proxy] platform {platform} ok: {_mask_ip_port(host + ':' + str(port))} cost={cost_ms}ms", flush=True)
        
        return result
        
    except Exception as e:
        cost_ms = int(round(time.time() * 1000)) - start
        print(f"[Proxy] platform {platform} test failed: {_mask_ip_port(host + ':' + str(port))} err={type(e).__name__}", flush=True)
        return {
            "ok": False,
            "blocked": False,
            "cost_ms": cost_ms,
            "error": str(e),
        }


def _query_ip_location(ip: str, timeout: int = 5) -> dict:
    """
    查询IP的实际地理位置
    
    Args:
        ip: IP地址
        timeout: 超时时间（秒）
    
    Returns:
        dict: {
            "country": "国家",
            "region": "地区",
            "city": "城市",
            "isp": "运营商",
            "success": bool
        }
    """
    try:
        # 使用 ip-api.com 查询IP位置（免费，支持中文）
        resp = requests.get(
            f"http://ip-api.com/json/{ip}?lang=zh-CN",
            timeout=timeout
        )
        data = resp.json()
        
        if data.get("status") == "success":
            return {
                "success": True,
                "country": data.get("country", ""),
                "region": data.get("regionName", ""),
                "city": data.get("city", ""),
                "isp": data.get("isp", ""),
                "lat": data.get("lat", 0),
                "lon": data.get("lon", 0),
            }
        else:
            return {"success": False, "error": data.get("message", "Unknown error")}
    except Exception as e:
        return {"success": False, "error": str(e)}


def _verify_proxy_location(host: str, port: int, expected_city: str = "", expected_region: str = "") -> dict:
    """
    验证代理IP的实际地理位置是否符合预期
    
    Args:
        host: 代理IP
        port: 代理端口
        expected_city: 预期城市
        expected_region: 预期地区
    
    Returns:
        dict: {
            "match": bool,
            "actual_city": str,
            "expected_city": str,
            "location": dict
        }
    """
    print(f"[Proxy] 验证IP地理位置: {host}:{port}", flush=True)
    
    # 查询IP位置
    location = _query_ip_location(host)
    
    if not location.get("success"):
        print(f"[Proxy] ⚠️  IP位置查询失败: {location.get('error')}", flush=True)
        return {
            "match": False,
            "error": location.get("error"),
            "location": location
        }
    
    actual_city = location.get("city", "")
    actual_region = location.get("region", "")
    
    print(f"[Proxy] IP实际位置: {actual_city}, {actual_region}, {location.get('country')}", flush=True)
    print(f"[Proxy] IP运营商: {location.get('isp')}", flush=True)
    
    # 检查是否匹配
    match = True
    if expected_city:
        # 标准化城市名（去掉"市"等后缀）
        actual_normalized = actual_city.replace("市", "").strip()
        expected_normalized = expected_city.replace("市", "").strip()
        
        if actual_normalized != expected_normalized:
            match = False
            print(f"[Proxy] ⚠️  位置不匹配！预期: {expected_city}, 实际: {actual_city}", flush=True)
        else:
            print(f"[Proxy] ✓ 位置匹配: {actual_city}", flush=True)
    
    return {
        "match": match,
        "actual_city": actual_city,
        "actual_region": actual_region,
        "expected_city": expected_city,
        "expected_region": expected_region,
        "location": location
    }


_PROXY_CACHE_TTL_SECONDS = 20 * 60
_resolved_proxy_cache = {}


def _resolve_shenlong_proxy(proxy_payload: dict, platform: str = None) -> dict:
    test_url = 'http://myip.ipip.net'
    city = str(proxy_payload.get('city') or '').strip()
    region_code = str(proxy_payload.get('regionCode') or '').strip()
    api_url = str(proxy_payload.get('apiUrl') or '').strip()
    product_key = str(proxy_payload.get('productKey') or '').strip()
    signature = str(proxy_payload.get('signature') or '').strip()
    isp = str(proxy_payload.get('isp') or '').strip()
    publish_task_id = str(proxy_payload.get('publish_task_id') or '').strip()
    
    # 获取平台信息用于测试
    target_platform = str(platform or proxy_payload.get('platform') or '').lower().strip()

    if not product_key:
        raise Exception('缺少神龙产品Key')
    if not signature:
        raise Exception('缺少神龙签名')

    if region_code and region_code.isdigit() and len(region_code) == 6:
        if region_code.endswith('0000'):
            region_code = ''
        elif not region_code.endswith('00'):
            region_code = region_code[:4] + '00'

    cache_key = ''
    if publish_task_id:
        cache_key = f"publish_task:{publish_task_id}:area:{region_code or '-'}:isp:{isp or '-'}"
        now = int(time.time())
        cached = _resolved_proxy_cache.get(cache_key)
        if isinstance(cached, dict) and cached.get('expire_at', 0) > now and cached.get('server'):
            server = str(cached.get('server') or '').strip()
            if server:
                print(f"[Proxy] cache hit: task={publish_task_id} area={region_code or '-'} isp={isp or '-'}", flush=True)
                return {'server': server}

    request_url = api_url or 'http://api.shenlongip.com/ip'
    params = {
        'key': product_key,
        'sign': signature,
        'count': 1,
        'pattern': 'json',
        'mr': 1,
    }
    if region_code:
        params['area'] = region_code
    if isp:
        params['isp'] = isp

    payload = None
    session = requests.Session()
    session.trust_env = False
    resp = session.get(
        request_url,
        params=params,
        headers={
            'User-Agent': 'Mozilla/5.0',
            'Accept': 'application/json',
        },
        timeout=15,
    )
    content_type = (resp.headers.get('content-type') or '').lower()
    raw_text = resp.text or ''
    try:
        if 'application/json' in content_type or raw_text.strip().startswith('{') or raw_text.strip().startswith('['):
            payload = resp.json()
    except Exception:
        payload = None
    if isinstance(payload, dict) and payload.get('code') is not None:
        try:
            api_code = int(payload.get('code'))
        except Exception:
            api_code = -1
        if api_code != 200:
            raise Exception(f"代理提取失败: code={api_code} msg={str(payload.get('msg') or '').strip() or 'unknown'}")
    elif resp.status_code >= 400:
        raise Exception(f"代理提取失败: HTTP {resp.status_code}")

    def collect_ip_ports(data_list, city_filter: str):
        ips = []
        for item in data_list:
            if isinstance(item, str):
                for ip_port in _extract_ip_ports(item):
                    ips.append(ip_port)
                continue
            if not isinstance(item, dict):
                continue
            item_city = str(item.get('city') or item.get('area') or '').strip()
            if city_filter and item_city and item_city != city_filter:
                continue
            ip = str(item.get('ip') or item.get('host') or item.get('proxy_ip') or '').strip()
            port = str(item.get('port') or item.get('proxy_port') or '').strip()
            if ip and port:
                ips.append(f"{ip}:{port}")
            proxy = str(item.get('proxy') or item.get('ip_port') or '').strip()
            if proxy:
                for ip_port in _extract_ip_ports(proxy):
                    ips.append(ip_port)
        return ips

    ip_ports = []
    if payload is not None:
        if isinstance(payload, dict):
            if isinstance(payload.get('data'), list):
                ip_ports = collect_ip_ports(payload.get('data'), '')
            elif isinstance(payload.get('list'), list):
                ip_ports = collect_ip_ports(payload.get('list'), '')
            elif payload.get('ip') and payload.get('port'):
                ip_ports = collect_ip_ports([payload], '')
        elif isinstance(payload, list):
            ip_ports = collect_ip_ports(payload, '')
    else:
        ip_ports = _extract_ip_ports(raw_text)

    if not ip_ports:
        raise Exception('代理提取结果为空')

    random.shuffle(ip_ports)
    candidates = ip_ports[: min(10, len(ip_ports))]

    print(f"[Proxy] shenlong resolved: city={city or '-'} area={region_code or '-'} candidates={len(candidates)}/{len(ip_ports)}", flush=True)

    for ip_port in candidates:
        try:
            host, port_str = ip_port.split(':', 1)
            port = int(port_str)
        except Exception:
            continue

        # 首先测试基础连接
        if not _test_proxy_connectivity(test_url, host, port, timeout=10):
            continue
        
        # 如果指定了平台，测试平台可用性
        if target_platform:
            platform_result = _test_proxy_for_platform(host, port, target_platform, timeout=15)
            if platform_result.get('blocked'):
                print(f"[Proxy] platform {target_platform} blocked, trying next...", flush=True)
                continue
            if not platform_result.get('ok') and platform_result.get('cost_ms', 0) > 10000:
                print(f"[Proxy] platform {target_platform} too slow ({platform_result.get('cost_ms')}ms), trying next...", flush=True)
                continue
        
        # 🔧 验证IP地理位置
        expected_city = city or ""
        location_result = _verify_proxy_location(host, port, expected_city)
        
        # 如果位置不匹配，给出警告但继续使用
        if not location_result.get("match") and expected_city:
            actual_city = location_result.get("actual_city", "未知")
            print(f"[Proxy] ⚠️  位置不匹配警告：预期 {expected_city}, 实际 {actual_city}", flush=True)
            print(f"[Proxy] 💡 这可能导致微信显示的位置不正确", flush=True)
            # 不阻止使用，只是警告
        
        server = f"http://{host}:{port}"
        if cache_key:
            _resolved_proxy_cache[cache_key] = {
                'server': server,
                'expire_at': int(time.time()) + _PROXY_CACHE_TTL_SECONDS,
            }
            print(f"[Proxy] cache set: task={publish_task_id} ttl={_PROXY_CACHE_TTL_SECONDS}s", flush=True)
        return {'server': server}

    raise Exception('未找到可用代理IP')


def validate_video_file(video_path: str) -> bool:
    """验证视频文件是否有效"""
    if not video_path:
        return False
    
    if not os.path.exists(video_path):
        return False
    
    if not os.path.isfile(video_path):
        return False
    
    valid_extensions = ['.mp4', '.mov', '.avi', '.mkv', '.flv', '.wmv', '.webm']
    ext = os.path.splitext(video_path)[1].lower()
    if ext not in valid_extensions:
        return False
    
    if os.path.getsize(video_path) < 1024:
        return False
    
    return True

# 创建 Flask 应用
app = Flask(__name__)
CORS(app)

# 配置日志以显示所有 HTTP 请求
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# 让 werkzeug 日志显示
werkzeug_logger = logging.getLogger('werkzeug')
werkzeug_logger.setLevel(logging.INFO)
# 添加 StreamHandler 确保输出到控制台
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
werkzeug_logger.addHandler(handler)
logging.getLogger('urllib3').setLevel(logging.WARNING)

# 添加请求钩子，打印所有收到的请求
@app.before_request
def log_request_info():
    """在处理每个请求前打印详细信息"""
    print(f"\n{'='*60}", flush=True)
    print(f"[HTTP Request] {request.method} {request.path}", flush=True)
    print(f"[HTTP Request] From: {request.remote_addr}", flush=True)
    if request.content_type and 'json' in request.content_type:
        try:
            data = request.get_json(silent=True)
            if data:
                # 打印部分参数，避免太长
                keys = list(data.keys()) if data else []
                print(f"[HTTP Request] JSON keys: {keys}", flush=True)
        except:
            pass
    print(f"{'='*60}\n", flush=True)

# 全局配置
HEADLESS_MODE = os.environ.get('HEADLESS', 'true').lower() == 'true'
print(f"[Config] HEADLESS env value: '{os.environ.get('HEADLESS', 'NOT SET')}'", flush=True)
print(f"[Config] HEADLESS_MODE: {HEADLESS_MODE}", flush=True)

# Node.js API 配置
NODEJS_API_BASE_URL = os.environ.get('NODEJS_API_URL', 'http://localhost:3000')
INTERNAL_API_KEY = os.environ.get('INTERNAL_API_KEY', 'internal-api-key-default')
print(f"[API Config] Node.js API: {NODEJS_API_BASE_URL}", flush=True)


class NodeApiError(Exception):
    """用于把 Node 内部接口的错误状态码/内容透传给调用方。"""

    def __init__(self, status_code: int, payload: dict):
        super().__init__(payload.get("error") or payload.get("message") or "Node API error")
        self.status_code = status_code
        self.payload = payload


def call_nodejs_api(method: str, endpoint: str, data: dict = None, params: dict = None) -> dict:
    """调用 Node.js 内部 API"""
    url = f"{NODEJS_API_BASE_URL}/api/internal{endpoint}"
    headers = {
        'Content-Type': 'application/json',
        'X-Internal-API-Key': INTERNAL_API_KEY,
    }
    
    try:
        if method.upper() == 'GET':
            response = requests.get(url, headers=headers, params=params, timeout=30)
        elif method.upper() == 'POST':
            response = requests.post(url, headers=headers, json=data, timeout=30)
        else:
            raise ValueError(f"Unsupported HTTP method: {method}")

        # 兼容 Node 可能返回非 JSON 的情况
        try:
            payload = response.json()
        except Exception:
            payload = {
                "success": False,
                "error": "Node.js API 返回非 JSON 响应",
                "status": response.status_code,
                "text": (response.text or "")[:2000],
                "url": url,
                "endpoint": endpoint,
            }

        if response.status_code >= 400:
            # 把真实状态码/返回体抛出去，由路由决定如何返回给前端
            if isinstance(payload, dict):
                payload.setdefault("success", False)
                payload.setdefault("status", response.status_code)
                payload.setdefault("url", url)
                payload.setdefault("endpoint", endpoint)
            raise NodeApiError(response.status_code, payload if isinstance(payload, dict) else {
                "success": False,
                "error": "Node.js API 调用失败",
                "status": response.status_code,
                "data": payload,
                "url": url,
                "endpoint": endpoint,
            })

        return payload
    except requests.exceptions.RequestException as e:
        # 连接失败/超时等（此时通常拿不到 response）
        print(f"[API Error] 调用 Node.js API 失败: {e}", flush=True)
        raise NodeApiError(502, {
            "success": False,
            "error": f"无法连接 Node.js API: {str(e)}",
            "status": 502,
            "url": url,
            "endpoint": endpoint,
        })


# ==================== 签名相关（小红书专用） ====================

@app.route("/sign", methods=["POST"])
def sign_endpoint():
    """小红书签名接口"""
    try:
        from platforms.xiaohongshu import XiaohongshuPublisher
        
        data = request.json
        publisher = XiaohongshuPublisher(headless=True)
        result = asyncio.run(publisher.get_sign(
            data.get("uri", ""),
            data.get("data"),
            data.get("a1", ""),
            data.get("web_session", "")
        ))
        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"error": str(e)}), 500


# ==================== 代理测试接口 ====================

@app.route("/proxy/test", methods=["POST"])
def test_proxy():
    """
    测试代理配置
    
    请求体:
    {
        "provider": "shenlong",           # 代理提供商
        "productKey": "xxx",              # 神龙产品Key
        "signature": "xxx",               # 神龙签名
        "apiUrl": "http://...",           # API地址（可选）
        "city": "城市",                   # 城市（可选）
        "regionCode": "区域代码",          # 区域代码（可选）
        "isp": "运营商",                  # 运营商（可选）
        "platform": "douyin"              # 目标平台（可选，用于测试平台可用性）
    }
    
    响应:
    {
        "success": true,
        "proxy": "http://x.x.x.x:port",
        "platform_test": {
            "ok": true,
            "blocked": false,
            "cost_ms": 1234
        }
    }
    """
    try:
        data = request.json
        provider = str(data.get('provider') or 'shenlong').strip().lower()
        platform = str(data.get('platform') or '').strip().lower()
        
        print(f"[Proxy Test] 测试代理: provider={provider}, platform={platform or 'default'}")
        
        if provider == 'shenlong':
            # 使用神龙代理解析
            proxy_config = _resolve_shenlong_proxy(data, platform=platform)
            server = proxy_config.get('server', '')
            
            result = {
                "success": True,
                "proxy": server,
                "provider": provider,
            }
            
            # 如果指定了平台，测试平台可用性
            if platform and server:
                # 从 server 中提取 host 和 port
                try:
                    server_clean = server.replace('http://', '').replace('https://', '')
                    host, port_str = server_clean.split(':')
                    port = int(port_str)
                    
                    platform_result = _test_proxy_for_platform(host, port, platform)
                    result["platform_test"] = platform_result
                except Exception as e:
                    result["platform_test"] = {
                        "ok": False,
                        "error": str(e)
                    }
            
            # 🔧 添加IP地理位置信息
            if server:
                try:
                    server_clean = server.replace('http://', '').replace('https://', '')
                    host = server_clean.split(':')[0]
                    
                    # 查询IP位置
                    location = _query_ip_location(host)
                    if location.get("success"):
                        result["ip_location"] = {
                            "country": location.get("country"),
                            "region": location.get("region"),
                            "city": location.get("city"),
                            "isp": location.get("isp"),
                        }
                        
                        # 检查是否匹配预期城市
                        expected_city = data.get('city', '').strip()
                        if expected_city:
                            actual_city = location.get("city", "")
                            actual_normalized = actual_city.replace("市", "").strip()
                            expected_normalized = expected_city.replace("市", "").strip()
                            
                            if actual_normalized != expected_normalized:
                                result["location_match"] = False
                                result["location_warning"] = f"IP实际位置({actual_city})与预期({expected_city})不符"
                            else:
                                result["location_match"] = True
                except Exception as e:
                    result["ip_location_error"] = str(e)
            
            return jsonify(result)
        else:
            return jsonify({
                "success": False,
                "error": f"不支持的代理提供商: {provider}"
            }), 400
            
    except Exception as e:
        traceback.print_exc()
        return jsonify({
            "success": False,
            "error": str(e)
        }), 500


@app.route("/proxy/platforms", methods=["GET"])
def get_supported_platforms():
    """获取支持的平台列表"""
    return jsonify({
        "platforms": [
            {"id": "douyin", "name": "抖音", "test_url": "https://creator.douyin.com/"},
            {"id": "xiaohongshu", "name": "小红书", "test_url": "https://creator.xiaohongshu.com/"},
            {"id": "kuaishou", "name": "快手", "test_url": "https://cp.kuaishou.com/"},
            {"id": "weixin", "name": "视频号", "test_url": "https://channels.weixin.qq.com/"},
        ]
    })


# ==================== 统一发布接口 ====================

@app.route("/publish", methods=["POST"])
def publish_video():
    """
    统一发布接口
    
    请求体:
    {
        "platform": "douyin",           # douyin | xiaohongshu | weixin | kuaishou
        "cookie": "cookie字符串或JSON",
        "title": "视频标题",
        "description": "视频描述（可选）",
        "video_path": "视频文件绝对路径",
        "cover_path": "封面图片绝对路径（可选）",
        "tags": ["话题1", "话题2"],
        "post_time": "定时发布时间（可选，格式：2024-01-20 12:00:00）",
        "location": "位置（可选，默认：重庆市）"
    }
    
    响应:
    {
        "success": true,
        "platform": "douyin",
        "video_id": "xxx",
        "video_url": "xxx",
        "message": "发布成功"
    }
    """
    try:
        data = request.json
        
        # 获取参数
        platform = data.get("platform", "").lower()
        cookie_str = data.get("cookie", "")
        title = data.get("title", "")
        description = data.get("description", "")
        video_path = data.get("video_path", "")
        cover_path = data.get("cover_path")
        tags = data.get("tags", [])
        post_time = data.get("post_time")
        location = data.get("location", "重庆市")
        
        # 🔧 如果启用了代理，自动使用代理所在地区作为发布位置
        proxy_payload = data.get('proxy')
        if isinstance(proxy_payload, dict) and proxy_payload.get('enabled'):
            # 优先级：city > regionName > 保持原值
            proxy_city = proxy_payload.get('city', '').strip()
            proxy_region = proxy_payload.get('regionName', '').strip()
            
            if proxy_city:
                location = proxy_city
                print(f"[Publish] 💡 使用代理地区作为发布位置: {location}", flush=True)
            elif proxy_region:
                location = proxy_region
                print(f"[Publish] 💡 使用代理区域作为发布位置: {location}", flush=True)
        
        # 调试日志
        print(f"[Publish] 收到请求: platform={platform}, title={title}, video_path={video_path}")
        
        # 参数验证
        if not platform:
            print("[Publish] 错误: 缺少 platform 参数")
            return jsonify({"success": False, "error": "缺少 platform 参数"}), 400
        if platform not in PLATFORM_MAP:
            print(f"[Publish] 错误: 不支持的平台 {platform}")
            return jsonify({
                "success": False, 
                "error": f"不支持的平台: {platform}，支持: {list(PLATFORM_MAP.keys())}"
            }), 400
        if not cookie_str:
            print("[Publish] 错误: 缺少 cookie 参数")
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        if not title:
            print("[Publish] 错误: 缺少 title 参数")
            return jsonify({"success": False, "error": "缺少 title 参数"}), 400
        if not video_path:
            print("[Publish] 错误: 缺少 video_path 参数")
            return jsonify({"success": False, "error": "缺少 video_path 参数"}), 400
        
        # 视频文件验证（增加详细信息）
        if not os.path.exists(video_path):
            print(f"[Publish] 错误: 视频文件不存在: {video_path}")
            return jsonify({"success": False, "error": f"视频文件不存在: {video_path}"}), 400
        if not os.path.isfile(video_path):
            print(f"[Publish] 错误: 路径不是文件: {video_path}")
            return jsonify({"success": False, "error": f"路径不是文件: {video_path}"}), 400
        
        # 解析发布时间
        publish_date = parse_datetime(post_time) if post_time else None
        
        # 创建发布参数
        params = PublishParams(
            title=title,
            video_path=video_path,
            description=description,
            cover_path=cover_path,
            tags=tags,
            publish_date=publish_date,
            location=location
        )
        
        print("=" * 60)
        print(f"[Publish] 平台: {platform}")
        print(f"[Publish] 标题: {title}")
        print(f"[Publish] 视频: {video_path}")
        print(f"[Publish] 封面: {cover_path}")
        print(f"[Publish] 话题: {tags}")
        print(f"[Publish] 定时: {publish_date}")
        print("=" * 60)
        
        # 获取对应平台的发布器
        PublisherClass = get_publisher(platform)
        publisher = PublisherClass(headless=HEADLESS_MODE)

        proxy_payload = data.get('proxy')
        if isinstance(proxy_payload, dict) and proxy_payload.get('enabled'):
            provider = str(proxy_payload.get('provider') or 'shenlong').strip().lower()
            if provider == 'shenlong':
                proxy_payload_with_task = dict(proxy_payload)
                if data.get('publish_task_id') is not None:
                    proxy_payload_with_task['publish_task_id'] = data.get('publish_task_id')
                # 传递平台参数，用于测试代理对特定平台的可用性
                publisher.proxy_config = _resolve_shenlong_proxy(proxy_payload_with_task, platform=platform)
        
        # 执行发布
        result = asyncio.run(publisher.run(cookie_str, params))
        
        response_data = {
            "success": result.success,
            "platform": result.platform,
            "video_id": result.video_id,
            "video_url": result.video_url,
            "message": result.message,
            "error": result.error,
            "need_captcha": result.need_captcha,
            "captcha_type": result.captcha_type,
            "screenshot_base64": result.screenshot_base64,
            "page_url": result.page_url,
            "status": result.status,
            "screenshot_path": getattr(result, 'screenshot_path', '') or ''
        }
        
        # 如果需要验证码，打印明确的日志
        if result.need_captcha:
            print(f"[Publish] 需要验证码: type={result.captcha_type}")
        
        return jsonify(response_data)
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


# ==================== AI 辅助发布接口 ====================

# 存储活跃的发布会话
active_publish_sessions = {}

@app.route("/publish/ai-assisted", methods=["POST"])
def publish_ai_assisted():
    """
    AI 辅助发布接口
    
    与普通发布接口的区别：
    1. 发布过程中会返回截图供 AI 分析
    2. 如果检测到需要验证码，返回截图和状态，等待外部处理
    3. 支持继续发布（输入验证码后）
    
    请求体:
    {
        "platform": "douyin",
        "cookie": "cookie字符串",
        "title": "视频标题",
        "video_path": "视频文件路径",
        ...
        "return_screenshot": true  // 是否返回截图
    }
    
    响应:
    {
        "success": true/false,
        "status": "success|failed|need_captcha|processing",
        "screenshot_base64": "...",  // 当前页面截图
        "page_url": "...",
        ...
    }
    """
    # 立即打印请求日志，确保能看到
    print("\n" + "!" * 60, flush=True)
    print("!!! [AI-Assisted Publish] 收到请求 !!!", flush=True)
    print("!" * 60 + "\n", flush=True)
    
    try:
        data = request.json
        print(f"[AI-Assisted Publish] 请求数据: platform={data.get('platform')}, title={data.get('title')}", flush=True)
        
        # 获取参数
        platform = data.get("platform", "").lower()
        cookie_str = data.get("cookie", "")
        title = data.get("title", "")
        description = data.get("description", "")
        video_path = data.get("video_path", "")
        cover_path = data.get("cover_path")
        tags = data.get("tags", [])
        post_time = data.get("post_time")
        location = data.get("location", "重庆市")
        
        # 🔧 如果启用了代理，自动使用代理所在地区作为发布位置
        proxy_payload = data.get('proxy')
        if isinstance(proxy_payload, dict) and proxy_payload.get('enabled'):
            # 优先级：city > regionName > 保持原值
            proxy_city = proxy_payload.get('city', '').strip()
            proxy_region = proxy_payload.get('regionName', '').strip()
            
            if proxy_city:
                location = proxy_city
                print(f"[AI-Assisted Publish] 💡 使用代理地区作为发布位置: {location}", flush=True)
            elif proxy_region:
                location = proxy_region
                print(f"[AI-Assisted Publish] 💡 使用代理区域作为发布位置: {location}", flush=True)
        
        return_screenshot = data.get("return_screenshot", True)
        # 支持请求级别的 headless 参数，用于验证码场景下的有头浏览器模式
        headless = data.get("headless", HEADLESS_MODE)
        if isinstance(headless, str):
            headless = headless.lower() == 'true'
        
        # 参数验证
        if not platform:
            return jsonify({"success": False, "error": "缺少 platform 参数"}), 400
        if platform not in PLATFORM_MAP:
            return jsonify({"success": False, "error": f"不支持的平台: {platform}"}), 400
        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        if not title:
            return jsonify({"success": False, "error": "缺少 title 参数"}), 400
        if not video_path or not os.path.exists(video_path):
            return jsonify({"success": False, "error": f"视频文件不存在: {video_path}"}), 400
        
        # 解析发布时间
        publish_date = parse_datetime(post_time) if post_time else None
        
        # 创建发布参数
        params = PublishParams(
            title=title,
            video_path=video_path,
            description=description,
            cover_path=cover_path,
            tags=tags,
            publish_date=publish_date,
            location=location
        )
        
        print("=" * 60)
        print(f"[AI Publish] 平台: {platform}")
        print(f"[AI Publish] 标题: {title}")
        print(f"[AI Publish] 视频: {video_path}")
        print(f"[AI Publish] Headless: {headless}")
        print("=" * 60)
        
        # 获取对应平台的发布器
        PublisherClass = get_publisher(platform)
        publisher = PublisherClass(headless=headless)  # 使用请求参数中的 headless 值

        proxy_payload = data.get('proxy')
        if isinstance(proxy_payload, dict) and proxy_payload.get('enabled'):
            provider = str(proxy_payload.get('provider') or 'shenlong').strip().lower()
            if provider == 'shenlong':
                proxy_payload_with_task = dict(proxy_payload)
                if data.get('publish_task_id') is not None:
                    proxy_payload_with_task['publish_task_id'] = data.get('publish_task_id')
                # 传递平台参数，用于测试代理对特定平台的可用性
                publisher.proxy_config = _resolve_shenlong_proxy(proxy_payload_with_task, platform=platform)
        try:
            publisher.user_id = int(data.get("user_id")) if data.get("user_id") is not None else None
        except Exception:
            publisher.user_id = None
        try:
            publisher.publish_task_id = int(data.get("publish_task_id")) if data.get("publish_task_id") is not None else None
        except Exception:
            publisher.publish_task_id = None
        try:
            publisher.publish_account_id = int(data.get("publish_account_id")) if data.get("publish_account_id") is not None else None
        except Exception:
            publisher.publish_account_id = None
        
        # 执行发布
        result = asyncio.run(publisher.run(cookie_str, params))
        
        response_data = {
            "success": result.success,
            "platform": result.platform,
            "video_id": result.video_id,
            "video_url": result.video_url,
            "message": result.message,
            "error": result.error,
            "need_captcha": result.need_captcha,
            "captcha_type": result.captcha_type,
            "status": result.status or ("success" if result.success else "failed"),
            "page_url": result.page_url,
            "screenshot_path": getattr(result, 'screenshot_path', '') or ''
        }
        
        # 如果请求返回截图
        if return_screenshot and result.screenshot_base64:
            response_data["screenshot_base64"] = result.screenshot_base64
        
        return jsonify(response_data)
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e), "status": "error"}), 500


# ==================== 批量发布接口 ====================

@app.route("/publish/batch", methods=["POST"])
def publish_batch():
    """
    批量发布接口 - 发布到多个平台
    
    请求体:
    {
        "platforms": ["douyin", "xiaohongshu"],
        "cookies": {
            "douyin": "cookie字符串",
            "xiaohongshu": "cookie字符串"
        },
        "title": "视频标题",
        "video_path": "视频文件绝对路径",
        ...
    }
    """
    try:
        data = request.json
        
        platforms = data.get("platforms", [])
        cookies = data.get("cookies", {})
        
        if not platforms:
            return jsonify({"success": False, "error": "缺少 platforms 参数"}), 400
        
        results = []
        
        for platform in platforms:
            platform = platform.lower()
            cookie_str = cookies.get(platform, "")
            
            if not cookie_str:
                results.append({
                    "platform": platform,
                    "success": False,
                    "error": f"缺少 {platform} 的 cookie"
                })
                continue
            
            try:
                # 创建参数
                params = PublishParams(
                    title=data.get("title", ""),
                    video_path=data.get("video_path", ""),
                    description=data.get("description", ""),
                    cover_path=data.get("cover_path"),
                    tags=data.get("tags", []),
                    publish_date=parse_datetime(data.get("post_time")),
                    location=data.get("location", "重庆市")
                )
                
                # 发布
                PublisherClass = get_publisher(platform)
                publisher = PublisherClass(headless=HEADLESS_MODE)
                result = asyncio.run(publisher.run(cookie_str, params))
                
                results.append({
                    "platform": result.platform,
                    "success": result.success,
                    "video_id": result.video_id,
                    "message": result.message,
                    "error": result.error
                })
            except Exception as e:
                results.append({
                    "platform": platform,
                    "success": False,
                    "error": str(e)
                })
        
        # 统计成功/失败数量
        success_count = sum(1 for r in results if r.get("success"))
        
        return jsonify({
            "success": success_count > 0,
            "total": len(platforms),
            "success_count": success_count,
            "fail_count": len(platforms) - success_count,
            "results": results
        })
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


# ==================== Cookie 验证接口 ====================

@app.route("/check_cookie", methods=["POST"])
def check_cookie():
    """检查 cookie 是否有效"""
    try:
        data = request.json
        platform = data.get("platform", "").lower()
        cookie_str = data.get("cookie", "")
        
        if not cookie_str:
            return jsonify({"valid": False, "error": "缺少 cookie 参数"}), 400
        
        # 目前只支持小红书的 cookie 验证
        if platform == "xiaohongshu":
            try:
                from platforms.xiaohongshu import XiaohongshuPublisher, XHS_SDK_AVAILABLE
                
                if XHS_SDK_AVAILABLE:
                    from xhs import XhsClient
                    publisher = XiaohongshuPublisher()
                    xhs_client = XhsClient(cookie_str, sign=publisher.sign_sync)
                    info = xhs_client.get_self_info()
                    
                    if info:
                        return jsonify({
                            "valid": True,
                            "user_info": {
                                "user_id": info.get("user_id"),
                                "nickname": info.get("nickname"),
                                "avatar": info.get("images")
                            }
                        })
            except Exception as e:
                return jsonify({"valid": False, "error": str(e)})
        
        # 其他平台返回格式正确但未验证
        return jsonify({
            "valid": True, 
            "message": "Cookie 格式正确，但未进行在线验证"
        })
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"valid": False, "error": str(e)})


# ==================== 获取作品列表接口 ====================

@app.route("/works", methods=["POST"])
def get_works():
    """
    获取作品列表
    
    请求体:
    {
        "platform": "douyin",           # douyin | xiaohongshu | kuaishou
        "cookie": "cookie字符串或JSON",
        "page": 0,                       # 页码（从0开始，可选，默认0）
        "page_size": 20                  # 每页数量（可选，默认20）
    }
    
    响应:
    {
        "success": true,
        "platform": "douyin",
        "works": [...],
        "total": 100,
        "has_more": true
    }
    """
    try:
        data = request.json
        
        platform = data.get("platform", "").lower()
        cookie_str = data.get("cookie", "")
        page = data.get("page", 0)
        page_size = data.get("page_size", 20)
        auto_paging = bool(data.get("auto_paging", False))
        
        print(f"[Works] 收到请求: platform={platform}, page={page}, page_size={page_size}, auto_paging={auto_paging}", flush=True)
        
        if not platform:
            return jsonify({"success": False, "error": "缺少 platform 参数"}), 400
        if platform not in PLATFORM_MAP:
            return jsonify({
                "success": False,
                "error": f"不支持的平台: {platform}，支持: {list(PLATFORM_MAP.keys())}"
            }), 400
        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        
        # 获取对应平台的发布器
        PublisherClass = get_publisher(platform)
        publisher = PublisherClass(headless=HEADLESS_MODE)
        
        # 执行获取作品
        if platform == "xiaohongshu" and auto_paging and hasattr(publisher, "get_all_works"):
            result = asyncio.run(publisher.get_all_works(cookie_str))
        else:
            result = asyncio.run(publisher.run_get_works(cookie_str, page, page_size))
        
        return jsonify(result.to_dict())
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


# ==================== 视频号同步作品每日数据（浏览器自动化） ====================

@app.route("/sync_weixin_account_works_daily_stats", methods=["POST"])
def sync_weixin_account_works_daily_stats():
    """
    纯浏览器批量同步视频号账号下所有（在库）作品的每日数据到 work_day_statistics。
    流程：打开 statistic/post → 单篇视频 → 近30天 → 遍历列表，按 exportId 匹配作品，
    匹配则点击查看 → 详情页近30天 → 下载表格 → 解析 CSV 存入 work_day_statistics。
    请求体: {
        "works": [{"work_id": 906, "platform_video_id": "export/xxx"}, ...],
        "cookie": "...",
        "show_browser": false
    }
    """
    try:
        data = request.json or {}
        works = data.get("works", [])
        cookie_str = data.get("cookie", "")

        if not works or not cookie_str:
            return jsonify({
                "success": False,
                "error": "缺少 works 或 cookie 参数",
            }), 400

        show_browser = data.get("show_browser", False)
        if isinstance(show_browser, str):
            show_browser = show_browser.lower() in ("true", "1", "yes")
        headless = not show_browser

        def save_fn(stats_list):
            if not stats_list:
                return {"inserted": 0, "updated": 0}
            return call_nodejs_api("POST", "/work-day-statistics/batch-dates", {"statistics": stats_list})

        def update_works_fn(updates):
            if not updates:
                return {"updated": 0}
            return call_nodejs_api("POST", "/works/batch-update-from-csv", {"updates": updates})

        publisher = WeixinPublisher(headless=headless)
        result = asyncio.run(publisher.sync_account_works_daily_stats_via_browser(
            cookie_str, works, save_fn=save_fn, update_works_fn=update_works_fn, headless=headless
        ))

        if not result.get("success"):
            return jsonify({
                "success": False,
                "error": result.get("error", "同步失败"),
            }), 200

        works_updated = result.get("works_updated", 0)
        msg = f"批量同步完成: 处理 {result.get('total_processed', 0)} 个作品, 跳过 {result.get('total_skipped', 0)} 个, 新增 {result.get('inserted', 0)} 条, 更新 {result.get('updated', 0)} 条"
        if works_updated > 0:
            msg += f", works 表更新 {works_updated} 条"
        return jsonify({
            "success": True,
            "message": msg,
            "total_processed": result.get("total_processed", 0),
            "total_skipped": result.get("total_skipped", 0),
            "inserted": result.get("inserted", 0),
            "updated": result.get("updated", 0),
            "works_updated": works_updated,
        })
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


@app.route("/sync_weixin_work_daily_stats", methods=["POST"])
def sync_weixin_work_daily_stats():
    """
    通过浏览器自动化同步单个视频号作品的每日数据到 work_day_statistics。
    请求体: { "work_id": 906, "platform_video_id": "export/xxx", "cookie": "..." }
    """
    try:
        data = request.json or {}
        work_id = data.get("work_id")
        platform_video_id = (data.get("platform_video_id") or "").strip()
        cookie_str = data.get("cookie", "")

        if not work_id or not platform_video_id or not cookie_str:
            return jsonify({
                "success": False,
                "error": "缺少 work_id、platform_video_id 或 cookie 参数",
            }), 400

        work_id = int(work_id)
        # show_browser=True 时显示浏览器窗口，便于观察点击操作
        show_browser = data.get("show_browser", True)
        if isinstance(show_browser, str):
            show_browser = show_browser.lower() in ("true", "1", "yes")
        headless = not show_browser
        print(f"[SyncWXDailyStats] headless={headless} (show_browser={show_browser})", flush=True)
        publisher = WeixinPublisher(headless=headless)
        result = asyncio.run(publisher.sync_work_daily_stats_via_browser(
            cookie_str, work_id, platform_video_id
        ))

        if not result.get("success"):
            return jsonify({
                "success": False,
                "error": result.get("error", "同步失败"),
            }), 200

        stats = result.get("statistics") or []
        if not stats:
            return jsonify({
                "success": True,
                "message": "无新数据需要保存",
                "inserted": 0,
                "updated": 0,
            })

        save_result = call_nodejs_api("POST", "/work-day-statistics/batch-dates", {
            "statistics": stats,
        })
        return jsonify({
            "success": True,
            "message": f"同步成功: 新增 {save_result.get('inserted', 0)} 条, 更新 {save_result.get('updated', 0)} 条",
            "inserted": save_result.get("inserted", 0),
            "updated": save_result.get("updated", 0),
        })
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


# ==================== 保存作品日统计数据接口 ====================

@app.route("/work_day_statistics", methods=["POST"])
def save_work_day_statistics():
    """
    保存作品每日统计数据
    当天的数据走更新流，日期变化走新增流
    
    请求体:
    {
        "statistics": [
            {
                "work_id": 1,
                "fans_count": 1000,
                "play_count": 5000,
                "like_count": 200,
                "comment_count": 50,
                "share_count": 30,
                "collect_count": 100
            },
            ...
        ]
    }
    
    响应:
    {
        "success": true,
        "inserted": 5,
        "updated": 3,
        "message": "保存成功"
    }
    """
    print("=" * 60, flush=True)
    print("[DEBUG] ===== 进入 save_work_day_statistics 方法 =====", flush=True)
    print(f"[DEBUG] 请求方法: {request.method}", flush=True)
    print(f"[DEBUG] 请求数据: {request.json}", flush=True)
    print("=" * 60, flush=True)
    
    try:
        data = request.json
        statistics_list = data.get("statistics", [])
        
        if not statistics_list:
            return jsonify({"success": False, "error": "缺少 statistics 参数"}), 400
        
        print(f"[WorkDayStatistics] 收到请求: {len(statistics_list)} 条统计数据")
        
        # 调用 Node.js API 保存数据
        result = call_nodejs_api('POST', '/work-day-statistics', {
            'statistics': statistics_list
        })
        
        print(f"[WorkDayStatistics] 完成: 新增 {result.get('inserted', 0)} 条, 更新 {result.get('updated', 0)} 条")
        
        return jsonify(result)
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


@app.route("/work_day_statistics/trend", methods=["GET"])
def get_statistics_trend():
    """
    获取数据趋势（用于 Dashboard 数据看板 和 数据分析页面）
    
    查询参数:
        user_id: 用户ID (必填)
        days: 天数 (可选，默认7天，最大30天) - 与 start_date/end_date 二选一
        start_date: 开始日期 (可选，格式 YYYY-MM-DD)
        end_date: 结束日期 (可选，格式 YYYY-MM-DD)
        account_id: 账号ID (可选，不填则查询所有账号)
    
    响应:
    {
        "success": true,
        "data": {
            "dates": ["01-16", "01-17", "01-18", ...],
            "fans": [100, 120, 130, ...],
            "views": [1000, 1200, 1500, ...],
            "likes": [50, 60, 70, ...],
            "comments": [10, 12, 15, ...],
            "shares": [5, 6, 8, ...],
            "collects": [20, 25, 30, ...]
        }
    }
    """
    try:
        user_id = request.args.get("user_id")
        days = request.args.get("days")
        start_date = request.args.get("start_date")
        end_date = request.args.get("end_date")
        account_id = request.args.get("account_id")
        
        if not user_id:
            return jsonify({"success": False, "error": "缺少 user_id 参数"}), 400
        
        # 调用 Node.js API 获取数据
        params = {"user_id": user_id}
        if days:
            params["days"] = days
        if start_date:
            params["start_date"] = start_date
        if end_date:
            params["end_date"] = end_date
        if account_id:
            params["account_id"] = account_id
        
        result = call_nodejs_api('GET', '/work-day-statistics/trend', params=params)
        
        return jsonify(result)
            
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


@app.route("/work_day_statistics/platforms", methods=["GET"])
def get_statistics_by_platform():
    """
    按平台分组获取统计数据（用于数据分析页面的平台对比）
    
    数据来源：
    - 粉丝数：从 platform_accounts 表获取（账号级别数据）
    - 播放量/点赞/评论/收藏：从 work_day_statistics 表按平台汇总
    - 粉丝增量：通过比较区间内最早和最新的粉丝数计算
    
    查询参数:
        user_id: 用户ID (必填)
        days: 天数 (可选，默认30天，最大30天) - 与 start_date/end_date 二选一
        start_date: 开始日期 (可选，格式 YYYY-MM-DD)
        end_date: 结束日期 (可选，格式 YYYY-MM-DD)
    
    响应:
    {
        "success": true,
        "data": [
            {
                "platform": "douyin",
                "fansCount": 1000,
                "fansIncrease": 50,
                "viewsCount": 5000,
                "likesCount": 200,
                "commentsCount": 30,
                "collectsCount": 100
            },
            ...
        ]
    }
    """
    try:
        user_id = request.args.get("user_id")
        days = request.args.get("days")
        start_date = request.args.get("start_date")
        end_date = request.args.get("end_date")
        
        if not user_id:
            return jsonify({"success": False, "error": "缺少 user_id 参数"}), 400
        
        # 调用 Node.js API 获取数据
        params = {"user_id": user_id}
        if days:
            params["days"] = days
        if start_date:
            params["start_date"] = start_date
        if end_date:
            params["end_date"] = end_date
        
        result = call_nodejs_api('GET', '/work-day-statistics/platforms', params=params)
        
        print(f"[PlatformStats] 返回 {len(result.get('data', []))} 个平台的数据")
        
        return jsonify(result)
            
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


@app.route("/work_day_statistics/batch", methods=["POST"])
def get_work_statistics_history():
    """
    批量获取作品的历史统计数据
    
    请求体:
    {
        "work_ids": [1, 2, 3],
        "start_date": "2025-01-01",  # 可选
        "end_date": "2025-01-21"      # 可选
    }
    
    响应:
    {
        "success": true,
        "data": {
            "1": [
                {"record_date": "2025-01-20", "play_count": 100, ...},
                {"record_date": "2025-01-21", "play_count": 150, ...}
            ],
            ...
        }
    }
    """
    try:
        data = request.json
        work_ids = data.get("work_ids", [])
        start_date = data.get("start_date")
        end_date = data.get("end_date")
        
        if not work_ids:
            return jsonify({"success": False, "error": "缺少 work_ids 参数"}), 400
        
        # 调用 Node.js API 获取数据
        request_data = {"work_ids": work_ids}
        if start_date:
            request_data["start_date"] = start_date
        if end_date:
            request_data["end_date"] = end_date
        
        result = call_nodejs_api('POST', '/work-day-statistics/batch', data=request_data)
        
        return jsonify(result)
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


@app.route("/work_day_statistics/overview", methods=["GET"])
def get_overview():
    """
    获取数据总览（账号列表和汇总统计）
    
    查询参数:
        user_id: 用户ID (必填)
    
    响应:
    {
        "success": true,
        "data": {
            "accounts": [
                {
                    "id": 1,
                    "nickname": "账号名称",
                    "username": "账号ID",
                    "avatarUrl": "头像URL",
                    "platform": "douyin",
                    "groupId": 1,
                    "fansCount": 1000,
                    "totalIncome": null,
                    "yesterdayIncome": null,
                    "totalViews": 5000,
                    "yesterdayViews": 100,
                    "yesterdayComments": 10,
                    "yesterdayLikes": 50,
                    "yesterdayFansIncrease": 5,
                    "updateTime": "2025-01-26T10:00:00Z",
                    "status": "active"
                },
                ...
            ],
            "summary": {
                "totalAccounts": 5,
                "totalIncome": 0,
                "yesterdayIncome": 0,
                "totalViews": 10000,
                "yesterdayViews": 200,
                "totalFans": 5000,
                "yesterdayComments": 20,
                "yesterdayLikes": 100,
                "yesterdayFansIncrease": 10
            }
        }
    }
    """
    try:
        user_id = request.args.get("user_id")
        
        if not user_id:
            return jsonify({"success": False, "error": "缺少 user_id 参数"}), 400
        
        # 调用 Node.js API 获取数据
        params = {"user_id": user_id}
        result = call_nodejs_api('GET', '/work-day-statistics/overview', params=params)
        
        return jsonify(result)
    except NodeApiError as e:
        # 透传 Node 的真实状态码/错误内容，避免所有错误都变成 500
        return jsonify(e.payload), e.status_code
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


# ==================== 获取评论列表接口 ====================

@app.route("/comments", methods=["POST"])
def get_comments():
    """
    获取作品评论
    
    请求体:
    {
        "platform": "douyin",           # douyin | xiaohongshu | kuaishou
        "cookie": "cookie字符串或JSON",
        "work_id": "作品ID",
        "cursor": ""                    # 分页游标（可选）
    }
    
    响应:
    {
        "success": true,
        "platform": "douyin",
        "work_id": "xxx",
        "comments": [...],
        "total": 50,
        "has_more": true,
        "cursor": "xxx"
    }
    """
    try:
        data = request.json
        
        platform = data.get("platform", "").lower()
        cookie_str = data.get("cookie", "")
        work_id = data.get("work_id", "")
        cursor = data.get("cursor", "")
        
        print(f"[Comments] 收到请求: platform={platform}, work_id={work_id}")
        
        if not platform:
            return jsonify({"success": False, "error": "缺少 platform 参数"}), 400
        if platform not in PLATFORM_MAP:
            return jsonify({
                "success": False,
                "error": f"不支持的平台: {platform}，支持: {list(PLATFORM_MAP.keys())}"
            }), 400
        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        if not work_id:
            return jsonify({"success": False, "error": "缺少 work_id 参数"}), 400
        
        # 获取对应平台的发布器
        PublisherClass = get_publisher(platform)
        publisher = PublisherClass(headless=HEADLESS_MODE)
        
        # 执行获取评论
        result = asyncio.run(publisher.run_get_comments(cookie_str, work_id, cursor))
        
        result_dict = result.to_dict()
        # 添加 cursor 到响应
        if hasattr(result, '__dict__') and 'cursor' in result.__dict__:
            result_dict['cursor'] = result.__dict__['cursor']
        
        return jsonify(result_dict)
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


# ==================== 获取所有作品评论接口 ====================

@app.route("/all_comments", methods=["POST"])
def get_all_comments():
    """
    获取所有作品的评论（一次性获取）
    
    请求体:
    {
        "platform": "douyin",           # douyin | xiaohongshu
        "cookie": "cookie字符串或JSON"
    }
    
    响应:
    {
        "success": true,
        "platform": "douyin",
        "work_comments": [
            {
                "work_id": "xxx",
                "title": "作品标题",
                "cover_url": "封面URL",
                "comments": [...]
            }
        ],
        "total": 5
    }
    """
    try:
        data = request.json
        
        platform = data.get("platform", "").lower()
        cookie_str = data.get("cookie", "")
        
        print(f"[AllComments] 收到请求: platform={platform}")
        
        if not platform:
            return jsonify({"success": False, "error": "缺少 platform 参数"}), 400
        if platform not in ['douyin', 'xiaohongshu']:
            return jsonify({
                "success": False,
                "error": f"该接口只支持 douyin 和 xiaohongshu 平台"
            }), 400
        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        
        # 获取对应平台的发布器
        PublisherClass = get_publisher(platform)
        publisher = PublisherClass(headless=HEADLESS_MODE)
        
        # 执行获取所有评论
        result = asyncio.run(publisher.get_all_comments(cookie_str))
        
        return jsonify(result)
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


# ==================== 登录状态检查接口 ====================

@app.route("/check_login", methods=["POST"])
def check_login():
    """
    检查 Cookie 登录状态（通过浏览器访问后台页面检测）
    
    请求体:
    {
        "platform": "douyin",           # douyin | xiaohongshu | kuaishou | weixin
        "cookie": "cookie字符串或JSON"
    }
    
    响应:
    {
        "success": true,
        "valid": true,                   # Cookie 是否有效
        "need_login": false,             # 是否需要重新登录
        "message": "登录状态有效"
    }
    """
    try:
        data = request.json
        
        platform = data.get("platform", "").lower()
        cookie_str = data.get("cookie", "")
        
        print(f"[CheckLogin] 收到请求: platform={platform}")
        
        if not platform:
            return jsonify({"success": False, "error": "缺少 platform 参数"}), 400
        if platform not in PLATFORM_MAP:
            return jsonify({
                "success": False,
                "error": f"不支持的平台: {platform}，支持: {list(PLATFORM_MAP.keys())}"
            }), 400
        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        
        # 获取对应平台的发布器
        PublisherClass = get_publisher(platform)
        publisher = PublisherClass(headless=HEADLESS_MODE)
        
        # 执行登录检查
        result = asyncio.run(publisher.check_login_status(cookie_str))
        
        return jsonify(result)
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({
            "success": False, 
            "valid": False, 
            "need_login": True,
            "error": str(e)
        }), 500


# ==================== 获取账号信息接口 ====================

@app.route("/account_info", methods=["POST"])
def get_account_info():
    """
    获取账号信息
    
    请求体:
    {
        "platform": "baijiahao",          # 平台
        "cookie": "cookie字符串或JSON"
    }
    
    响应:
    {
        "success": true,
        "account_id": "xxx",
        "account_name": "用户名",
        "avatar_url": "头像URL",
        "fans_count": 0,
        "works_count": 0
    }
    """
    try:
        data = request.json
        
        platform = data.get("platform", "").lower()
        cookie_str = data.get("cookie", "")
        
        print(f"[AccountInfo] 收到请求: platform={platform}")
        
        if not platform:
            return jsonify({"success": False, "error": "缺少 platform 参数"}), 400
        if platform not in PLATFORM_MAP:
            return jsonify({
                "success": False,
                "error": f"不支持的平台: {platform}，支持: {list(PLATFORM_MAP.keys())}"
            }), 400
        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        
        # 获取对应平台的发布器
        PublisherClass = get_publisher(platform)
        publisher = PublisherClass(headless=HEADLESS_MODE)
        
        # 检查是否有 get_account_info 方法
        if hasattr(publisher, 'get_account_info'):
            result = asyncio.run(publisher.get_account_info(cookie_str))
            return jsonify(result)
        else:
            return jsonify({
                "success": False,
                "error": f"平台 {platform} 不支持获取账号信息"
            }), 400
        
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


# ==================== 百家号作品每日数据辅助接口 ====================

@app.route("/baijiahao/article_stats", methods=["POST"])
def baijiahao_article_stats():
    """
    百家号：代理调用 /author/eco/statistics/articleListStatistic

    请求体:
    {
        "cookie": "...",
        "start_day": "YYYYMMDD",
        "end_day": "YYYYMMDD",
        "type": "small_video_v2|video|news",
        "num": 1,
        "count": 10
    }
    """
    try:
        data = request.json or {}
        cookie_str = data.get("cookie", "")
        start_day = data.get("start_day", "")
        end_day = data.get("end_day", "")
        stat_type = data.get("type", "video")
        num = int(data.get("num", 1) or 1)
        count = int(data.get("count", 10) or 10)

        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        if not start_day or not end_day:
            return jsonify({"success": False, "error": "缺少 start_day 或 end_day 参数"}), 400

        PublisherClass = get_publisher("baijiahao")
        publisher = PublisherClass(headless=HEADLESS_MODE)
        result = asyncio.run(
            publisher.get_article_stats(
                cookie_str,
                start_day=start_day,
                end_day=end_day,
                stat_type=stat_type,
                num=num,
                count=count,
            )
        )
        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


@app.route("/baijiahao/trend_data", methods=["POST"])
def baijiahao_trend_data():
    """
    百家号：代理调用 /author/eco/statistic/gettrenddata

    请求体:
    {
        "cookie": "...",
        "nid": "文章/视频 nid 或 article_id"
    }
    """
    try:
        data = request.json or {}
        cookie_str = data.get("cookie", "")
        nid = data.get("nid", "")

        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        if not nid:
            return jsonify({"success": False, "error": "缺少 nid 参数"}), 400

        PublisherClass = get_publisher("baijiahao")
        publisher = PublisherClass(headless=HEADLESS_MODE)
        result = asyncio.run(
            publisher.get_trend_data(
                cookie_str,
                nid=str(nid),
            )
        )
        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e)}), 500


@app.route("/baijiahao/app_statistic_v3", methods=["POST"])
def baijiahao_app_statistic_v3():
    """
    百家号用户每日数据：代理调用 appStatisticV3（账号近30天基础数据）。
    登录模式与打开后台一致：使用账号已存 Cookie。
    请求体: { "cookie": "...", "start_day": "YYYYMMDD", "end_day": "YYYYMMDD" }
    """
    try:
        data = request.json or {}
        cookie_str = data.get("cookie", "")
        start_day = data.get("start_day", "")
        end_day = data.get("end_day", "")

        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        if not start_day or not end_day:
            return jsonify({"success": False, "error": "缺少 start_day 或 end_day 参数"}), 400

        PublisherClass = get_publisher("baijiahao")
        publisher = PublisherClass(headless=HEADLESS_MODE)
        result = asyncio.run(
            publisher.get_app_statistic_v3(cookie_str, start_day=start_day, end_day=end_day)
        )
        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e), "errno": -1}), 500


@app.route("/baijiahao/fans_basic_info", methods=["POST"])
def baijiahao_fans_basic_info():
    """
    百家号用户每日数据：代理调用 getFansBasicInfo（近30天粉丝数据）。
    登录模式与打开后台一致：使用账号已存 Cookie。
    请求体: { "cookie": "...", "start": "YYYYMMDD", "end": "YYYYMMDD" }
    """
    try:
        data = request.json or {}
        cookie_str = data.get("cookie", "")
        start_str = data.get("start", "")
        end_str = data.get("end", "")

        if not cookie_str:
            return jsonify({"success": False, "error": "缺少 cookie 参数"}), 400
        if not start_str or not end_str:
            return jsonify({"success": False, "error": "缺少 start 或 end 参数"}), 400

        PublisherClass = get_publisher("baijiahao")
        publisher = PublisherClass(headless=HEADLESS_MODE)
        result = asyncio.run(
            publisher.get_fans_basic_info(cookie_str, start=start_str, end=end_str)
        )
        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"success": False, "error": str(e), "errno": -1}), 500


@app.route("/xiaohongshu/account_base", methods=["POST"])
def xiaohongshu_account_base():
    """
    小红书每日用户数据：代理调用创作者中心 account/base（账号概览近30日）。
    登录方式与打开后台一致：使用账号已存 Cookie。
    请求体: { "cookie": "..." }
    """
    try:
        data = request.json or {}
        cookie_str = data.get("cookie", "")
        if not cookie_str:
            return jsonify({"data": None, "code": 400, "error": "缺少 cookie 参数"}), 400
        PublisherClass = get_publisher("xiaohongshu")
        publisher = PublisherClass(headless=HEADLESS_MODE)
        result = asyncio.run(publisher.get_account_base(cookie_str))
        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"data": None, "code": 500, "error": str(e)}), 500


@app.route("/xiaohongshu/fans_overall_new", methods=["POST"])
def xiaohongshu_fans_overall_new():
    """
    小红书每日用户数据：代理调用创作者中心 fans/overall_new（粉丝趋势近30日）。
    登录方式与打开后台一致：使用账号已存 Cookie。
    请求体: { "cookie": "..." }
    """
    try:
        data = request.json or {}
        cookie_str = data.get("cookie", "")
        if not cookie_str:
            return jsonify({"data": None, "code": 400, "error": "缺少 cookie 参数"}), 400
        PublisherClass = get_publisher("xiaohongshu")
        publisher = PublisherClass(headless=HEADLESS_MODE)
        result = asyncio.run(publisher.get_fans_overall_new(cookie_str))
        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"data": None, "code": 500, "error": str(e)}), 500


@app.route("/xiaohongshu/account_overview", methods=["POST"])
def xiaohongshu_account_overview():
    """
    小红书每日用户数据：一次请求同时拉取 account/base 与 fans/overall_new（内部并行），减少耗时。
    请求体: { "cookie": "..." }
    返回: { "account_base": {...}, "fans_overall_new": {...} }
    """
    try:
        data = request.json or {}
        cookie_str = data.get("cookie", "")
        if not cookie_str:
            return jsonify({"data": None, "code": 400, "error": "缺少 cookie 参数"}), 400
        PublisherClass = get_publisher("xiaohongshu")
        publisher = PublisherClass(headless=HEADLESS_MODE)
        result = asyncio.run(publisher.get_account_overview(cookie_str))
        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"account_base": None, "fans_overall_new": None, "code": 500, "error": str(e)}), 500


@app.route("/xiaohongshu/note_base", methods=["POST"])
def xiaohongshu_note_base():
    """
    小红书每日作品数据：代理调用创作者中心 note/base（单条笔记数据）。
    登录方式与打开后台一致：使用账号已存 Cookie。
    请求体: { "cookie": "...", "note_id": "xxx" }
    """
    try:
        data = request.json or {}
        cookie_str = data.get("cookie", "")
        note_id = (data.get("note_id") or "").strip()

        if not cookie_str:
            return jsonify({"data": None, "code": 400, "error": "缺少 cookie 参数"}), 400
        if not note_id:
            return jsonify({"data": None, "code": 400, "error": "缺少 note_id 参数"}), 400

        PublisherClass = get_publisher("xiaohongshu")
        publisher = PublisherClass(headless=HEADLESS_MODE)
        result = asyncio.run(publisher.get_note_base(cookie_str, note_id))
        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"data": None, "code": 500, "error": str(e)}), 500


# ==================== 健康检查 ====================

@app.route("/health", methods=["GET"])
def health_check():
    """健康检查"""
    # 检查 xhs SDK 是否可用
    xhs_available = False
    try:
        from platforms.xiaohongshu import XHS_SDK_AVAILABLE
        xhs_available = XHS_SDK_AVAILABLE
    except:
        pass
    
    return jsonify({
        "status": "ok",
        "xhs_sdk": xhs_available,
        "supported_platforms": list(PLATFORM_MAP.keys()),
        "headless_mode": HEADLESS_MODE
    })


@app.route("/", methods=["GET"])
def index():
    """首页"""
    return jsonify({
        "name": "智媒通视频发布服务",
        "version": "1.2.0",
        "endpoints": {
            "GET /": "服务信息",
            "GET /health": "健康检查",
            "POST /publish": "发布视频",
            "POST /publish/batch": "批量发布",
            "POST /works": "获取作品列表",
            "POST /comments": "获取作品评论",
            "POST /all_comments": "获取所有作品评论",
            "POST /work_day_statistics": "保存作品每日统计数据",
            "POST /work_day_statistics/batch": "获取作品历史统计数据",
            "POST /check_cookie": "检查 Cookie",
            "POST /sign": "小红书签名"
        },
        "supported_platforms": list(PLATFORM_MAP.keys())
    })


# ==================== 命令行启动 ====================

def main():
    parser = argparse.ArgumentParser(description='智媒通视频发布服务')
    parser.add_argument('--port', type=int, default=5005, help='服务端口 (默认: 5005)')
    # 从环境变量读取 HOST，默认仅本地访问
    default_host = os.environ.get('PYTHON_HOST', os.environ.get('HOST', '127.0.0.1'))
    parser.add_argument('--host', type=str, default=default_host, help='监听地址 (默认: 127.0.0.1，可通过 HOST 环境变量配置)')
    parser.add_argument('--headless', type=str, default='true', help='是否无头模式 (默认: true)')
    parser.add_argument('--debug', action='store_true', help='调试模式')
    
    args = parser.parse_args()
    
    global HEADLESS_MODE
    HEADLESS_MODE = args.headless.lower() == 'true'
    
    # 检查 xhs SDK
    xhs_status = "未安装"
    try:
        from platforms.xiaohongshu import XHS_SDK_AVAILABLE
        xhs_status = "已安装" if XHS_SDK_AVAILABLE else "未安装"
    except:
        pass
    
    print("=" * 60)
    print("智媒通视频发布服务")
    print("=" * 60)
    print(f"XHS SDK: {xhs_status}")
    print(f"Headless 模式: {HEADLESS_MODE}")
    print(f"支持平台: {', '.join(PLATFORM_MAP.keys())}")
    print("=" * 60)
    print(f"启动服务: http://{args.host}:{args.port}")
    print("=" * 60)
    
    app.run(host=args.host, port=args.port, debug=bool(args.debug), threaded=True, use_reloader=False)


@app.route('/auto-reply', methods=['POST'])
def auto_reply():
    """
    微信视频号自动回复私信
    POST /auto-reply
    Body: {
        "platform": "weixin",
        "cookie": "..."
    }
    """
    try:
        data = request.json
        platform = data.get('platform', '').lower()
        cookie = data.get('cookie', '')
        
        if platform != 'weixin':
            return jsonify({
                'success': False,
                'error': '只支持微信视频号平台'
            }), 400
        
        if not cookie:
            return jsonify({
                'success': False,
                'error': '缺少 Cookie'
            }), 400
        
        print(f"[API] 接收自动回复请求: platform={platform}")
        
        # 创建 Publisher 实例
        publisher = WeixinPublisher(headless=HEADLESS_MODE)
        
        # 执行自动回复
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        result = loop.run_until_complete(publisher.auto_reply_private_messages(cookie))
        loop.close()
        
        print(f"[API] 自动回复结果: {result}")
        
        return jsonify(result)
    
    except Exception as e:
        print(f"[API] 自动回复异常: {e}")
        traceback.print_exc()
        return jsonify({
            'success': False,
            'error': str(e)
        }), 500


if __name__ == '__main__':
    main()
