#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Stage 7 独立运行脚本
从 Stage 6 结果开始，进行深度解构分析
支持指定 feature 和数量限制
"""

import os
import json
import logging
import argparse
import webbrowser
from pathlib import Path
from stage7_analyzer import Stage7DeconstructionAnalyzer
from stage8_similarity_analyzer import Stage8SimilarityAnalyzer
import visualize_stage78_with_deconstruction

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
    handlers=[
        logging.FileHandler('stage7_standalone.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)
logger = logging.getLogger(__name__)


def main():
    """主函数"""
    parser = argparse.ArgumentParser(
        description='Stage 7 深度解构分析（独立运行，支持流水线执行）',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
基础用法示例:
  # 只处理"墨镜"特征的前10个高分帖子
  python3 run_stage7.py --feature "墨镜" --max-notes 10

  # 处理"墨镜"和"耳环"两个特征，每个最多5个
  python3 run_stage7.py --feature "墨镜" "耳环" --max-notes 5

  # 按数据原始顺序处理前50个（不排序）
  python3 run_stage7.py --sort-by none --max-notes 50

  # 处理所有特征，按时间排序，前20个
  python3 run_stage7.py --sort-by time --max-notes 20

  # 只处理"墨镜"，按互动量排序，跳过前3个
  python3 run_stage7.py --feature "墨镜" --sort-by engagement --skip 3

  # 降低分数阈值，处理更多帖子
  python3 run_stage7.py --feature "墨镜" --min-score 6.0 --max-notes 30

流水线执行示例（推荐）:
  # 完整流水线: Stage 7 → Stage 8 → 可视化 → 自动打开浏览器
  python3 run_stage7.py --feature "墨镜" --max-notes 10 --run-stage8 --visualize

  # Stage 7 → Stage 8（不生成可视化）
  python3 run_stage7.py --feature "墨镜" --max-notes 10 --run-stage8

  # Stage 7 → 可视化（跳过 Stage 8）
  python3 run_stage7.py --feature "墨镜" --max-notes 10 --visualize

  # 完整流水线，不自动打开浏览器
  python3 run_stage7.py --feature "墨镜" --run-stage8 --visualize --no-open

  # 自定义 Stage 8 相似度权重
  python3 run_stage7.py --feature "墨镜" --run-stage8 --visualize \\
    --stage8-weight-embedding 0.7 --stage8-weight-semantic 0.3

  # 过滤低相似度特征
  python3 run_stage7.py --feature "墨镜" --run-stage8 --visualize \\
    --stage8-min-similarity 0.3

配置文件示例:
  # 使用配置文件（支持所有参数）
  python3 run_stage7.py --config pipeline_config.json

  # 配置文件示例内容（pipeline_config.json）:
  {
    "feature": ["墨镜"],
    "max_notes": 10,
    "timeout": 600,
    "run_stage8": true,
    "visualize": true,
    "stage8_weight_embedding": 0.5,
    "stage8_weight_semantic": 0.5
  }
        '''
    )

    # 输入输出配置
    parser.add_argument(
        '--input',
        default='output_v2/stage6_with_evaluations.json',
        help='Stage 6 结果文件路径（默认: output_v2/stage6_with_evaluations.json）'
    )
    parser.add_argument(
        '--output',
        default='output_v2/stage7_with_deconstruction.json',
        help='Stage 7 输出文件路径（默认: output_v2/stage7_with_deconstruction.json）'
    )

    # Feature 过滤（新增）
    parser.add_argument(
        '--feature',
        nargs='+',
        default=None,
        help='指定要处理的原始特征名称（可指定多个），如: --feature "墨镜" "耳环"。不指定则处理所有特征'
    )

    # 过滤参数
    parser.add_argument(
        '--min-score',
        type=float,
        default=0.8,
        help='最低分数阈值，只处理 >= 此分数的帖子（默认: 0.8）'
    )
    parser.add_argument(
        '--skip',
        type=int,
        default=0,
        help='跳过前 N 个帖子（默认: 0）'
    )
    parser.add_argument(
        '--max-notes',
        type=int,
        default=None,
        help='最多处理多少个帖子（默认: None 不限制）'
    )
    parser.add_argument(
        '--sort-by',
        choices=['none', 'score', 'time', 'engagement'],
        default='score',
        help='排序方式: none(不排序,保持数据原始顺序), score(评分), time(时间), engagement(互动量)（默认: score）'
    )

    # API 配置
    parser.add_argument(
        '--api-url',
        default='http://192.168.245.150:7000/what/analysis/single',
        help='解构 API 地址（默认: http://192.168.245.150:7000/what/analysis/single）'
    )
    parser.add_argument(
        '--timeout',
        type=int,
        default=800,
        help='API 超时时间（秒）（默认: 600，即10分钟）'
    )
    parser.add_argument(
        '--max-retries',
        type=int,
        default=3,
        help='API 最大重试次数（默认: 3）'
    )

    # 并发配置
    parser.add_argument(
        '--max-workers',
        type=int,
        default=5,
        help='并发处理数（默认: 5）'
    )

    # 从配置文件加载
    parser.add_argument(
        '--config',
        default=None,
        help='从 JSON 配置文件加载参数'
    )

    # 流水线控制参数
    parser.add_argument(
        '--run-stage8',
        action='store_true',
        help='Stage 7 完成后自动运行 Stage 8'
    )
    parser.add_argument(
        '--visualize',
        action='store_true',
        help='生成可视化结果'
    )
    parser.add_argument(
        '--open-browser',
        action='store_true',
        default=True,
        help='自动在浏览器中打开可视化结果（默认: True）'
    )
    parser.add_argument(
        '--no-open',
        action='store_true',
        help='禁用自动打开浏览器'
    )

    # Stage 8 输出配置
    parser.add_argument(
        '--stage8-output',
        default='output_v2/stage8_similarity_scores.json',
        help='Stage 8 输出文件路径（默认: output_v2/stage8_similarity_scores.json）'
    )

    # Stage 8 相似度配置
    parser.add_argument(
        '--stage8-weight-embedding',
        type=float,
        default=0.5,
        help='Stage 8 向量模型权重（默认: 0.5）'
    )
    parser.add_argument(
        '--stage8-weight-semantic',
        type=float,
        default=0.5,
        help='Stage 8 LLM 模型权重（默认: 0.5）'
    )
    parser.add_argument(
        '--stage8-min-similarity',
        type=float,
        default=0.0,
        help='Stage 8 最小相似度阈值（默认: 0.0）'
    )
    parser.add_argument(
        '--stage8-max-workers',
        type=int,
        default=5,
        help='Stage 8 最大并发数（默认: 5）'
    )

    # 可视化输出配置
    parser.add_argument(
        '--viz-output',
        default=None,
        help='可视化输出目录（默认: visualization/）'
    )

    args = parser.parse_args()

    # 如果提供了配置文件，加载配置
    if args.config:
        logger.info(f"从配置文件加载参数: {args.config}")
        with open(args.config, 'r', encoding='utf-8') as f:
            config = json.load(f)

        # 配置文件中的参数会覆盖命令行参数
        for key, value in config.items():
            setattr(args, key.replace('-', '_'), value)

    # 检查输入文件是否存在
    if not os.path.exists(args.input):
        logger.error(f"输入文件不存在: {args.input}")
        return

    # 加载 Stage 6 结果
    logger.info(f"加载 Stage 6 结果: {args.input}")
    with open(args.input, 'r', encoding='utf-8') as f:
        stage6_results = json.load(f)

    # 打印配置
    logger.info("=" * 60)
    logger.info("运行配置:")
    logger.info(f"  输入文件: {args.input}")
    logger.info(f"  输出文件: {args.output}")
    if args.feature:
        logger.info(f"  指定特征: {', '.join(args.feature)}")
    else:
        logger.info(f"  指定特征: 全部")
    logger.info(f"  API 地址: {args.api_url}")
    logger.info(f"  最低分数阈值: {args.min_score}")
    logger.info(f"  跳过前 N 个: {args.skip}")
    logger.info(f"  最多处理数: {args.max_notes if args.max_notes else '不限制'}")
    logger.info(f"  排序方式: {args.sort_by}")
    logger.info(f"  并发数: {args.max_workers}")
    logger.info(f"  API 超时: {args.timeout}秒")
    logger.info(f"  最大重试: {args.max_retries}次")
    logger.info("=" * 60)

    # 创建分析器
    analyzer = Stage7DeconstructionAnalyzer(
        api_url=args.api_url,
        max_workers=args.max_workers,
        max_notes=args.max_notes,
        min_score=args.min_score,
        skip_count=args.skip,
        sort_by=args.sort_by,
        timeout=args.timeout,
        max_retries=args.max_retries,
        output_dir=os.path.dirname(args.output) or 'output_v2',
        target_features=args.feature  # 传递 feature 过滤参数
    )

    # 运行分析
    try:
        stage7_results = analyzer.run(
            stage6_results=stage6_results,
            output_path=args.output
        )

        # 打印结果摘要
        logger.info("\n" + "=" * 60)
        logger.info("Stage 7 执行完成!")
        logger.info(f"  总匹配帖子数: {stage7_results['metadata']['total_matched_notes']}")
        logger.info(f"  实际处理数: {stage7_results['metadata']['processed_notes']}")
        logger.info(f"  成功: {stage7_results['metadata']['success_count']}")
        logger.info(f"  失败: {stage7_results['metadata']['failed_count']}")
        logger.info(f"  总耗时: {stage7_results['metadata']['processing_time_seconds']}秒")
        logger.info(f"  结果已保存: {args.output}")
        logger.info("=" * 60)

        # Stage 8: 相似度分析
        stage8_results = None
        if args.run_stage8:
            logger.info("\n" + "=" * 60)
            logger.info("开始执行 Stage 8 相似度分析...")
            logger.info("=" * 60)

            try:
                # 创建 Stage 8 分析器
                stage8_analyzer = Stage8SimilarityAnalyzer(
                    weight_embedding=args.stage8_weight_embedding,
                    weight_semantic=args.stage8_weight_semantic,
                    max_workers=args.stage8_max_workers,
                    min_similarity=args.stage8_min_similarity,
                    target_features=args.feature
                )

                # 运行 Stage 8 分析
                stage8_results = stage8_analyzer.run(
                    stage7_results=stage7_results,
                    output_path=args.stage8_output
                )

                # 打印 Stage 8 结果摘要
                logger.info("\n" + "=" * 60)
                logger.info("Stage 8 执行完成!")
                metadata = stage8_results['metadata']
                overall_stats = metadata['overall_statistics']

                logger.info(f"  处理帖子数: {overall_stats['total_notes']}")
                logger.info(f"  提取特征总数: {overall_stats['total_features_extracted']}")
                logger.info(f"  平均特征数/帖子: {overall_stats['avg_features_per_note']:.2f}")
                logger.info(f"  平均最高相似度: {overall_stats['avg_max_similarity']:.3f}")
                logger.info(f"  包含高相似度特征的帖子: {overall_stats['notes_with_high_similarity']}")
                logger.info(f"  总耗时: {metadata['processing_time_seconds']:.2f}秒")
                logger.info(f"  结果已保存: {args.stage8_output}")
                logger.info("=" * 60)

                # 打印 Top 5 高相似度特征示例
                if stage8_results['results']:
                    logger.info("\nTop 5 高相似度特征示例:")
                    all_features = []
                    for result in stage8_results['results']:
                        for feat in result['deconstructed_features'][:5]:
                            all_features.append({
                                'note_id': result['note_id'],
                                'feature_name': feat['feature_name'],
                                'dimension': feat['dimension'],
                                'similarity': feat['similarity_score']
                            })

                    # 按相似度排序，取 Top 5
                    all_features.sort(key=lambda x: x['similarity'], reverse=True)
                    for i, feat in enumerate(all_features[:5], 1):
                        logger.info(f"  {i}. [{feat['note_id'][:12]}...] "
                                   f"{feat['feature_name']} ({feat['dimension']}) "
                                   f"- 相似度: {feat['similarity']:.3f}")

            except Exception as e:
                logger.error(f"Stage 8 执行失败: {e}", exc_info=True)
                logger.warning("继续执行后续步骤...")

        # 可视化生成
        viz_path = None
        if args.visualize:
            logger.info("\n" + "=" * 60)
            logger.info("开始生成可视化结果...")
            logger.info("=" * 60)

            try:
                # 准备可视化所需的数据文件路径
                viz_args = [
                    '--stage6', args.input,
                    '--stage7', args.output
                ]

                # 如果有 Stage 8 结果，添加到参数中
                if stage8_results and args.stage8_output:
                    viz_args.extend(['--stage8', args.stage8_output])

                # 如果指定了可视化输出目录
                if args.viz_output:
                    viz_args.extend(['--output-dir', args.viz_output])

                # 调用可视化模块
                import sys
                original_argv = sys.argv
                try:
                    sys.argv = ['visualize_stage78_with_deconstruction.py'] + viz_args
                    viz_path = visualize_stage78_with_deconstruction.main()
                finally:
                    sys.argv = original_argv

                if viz_path:
                    logger.info("\n" + "=" * 60)
                    logger.info("可视化生成完成!")
                    logger.info(f"  可视化文件: {viz_path}")
                    logger.info("=" * 60)

                    # 自动打开浏览器
                    if args.open_browser and not args.no_open:
                        logger.info("\n正在打开浏览器...")
                        try:
                            # 使用 Path.as_uri() 来正确处理包含中文和特殊字符的路径
                            file_url = Path(viz_path).resolve().as_uri()
                            webbrowser.open(file_url)
                            logger.info("浏览器已打开")
                        except Exception as e:
                            logger.warning(f"无法自动打开浏览器: {e}")
                            logger.info(f"请手动打开: {os.path.abspath(viz_path)}")
                else:
                    logger.warning("可视化生成返回了空路径")

            except Exception as e:
                logger.error(f"可视化生成失败: {e}", exc_info=True)
                logger.warning("跳过可视化步骤")

        # 流水线执行完成
        logger.info("\n" + "=" * 60)
        logger.info("流水线执行完成!")
        logger.info("=" * 60)

    except Exception as e:
        logger.error(f"执行失败: {e}", exc_info=True)
        raise


if __name__ == '__main__':
    main()
