#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：functional_report_service.py
模块描述：功能测试报告核心服务，编排 Excel 读取 → 数据分析 → 图表生成 → 报告生成 完整流程

作者：czj
创建日期：2026-08-12
最后修改：2026-08-12
"""

import logging
import uuid
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple

from .functional.excel_reader import read_test_cases, read_bug_list, TestCase, Bug
from .functional.data_analyzer import (
    analyze_test_cases,
    analyze_bugs,
    link_bugs_to_cases,
    TestCaseAnalysis,
    BugAnalysis,
)
from .functional.data_extractor import (
    extract_testers,
    extract_test_lead,
    extract_server_info,
    extract_version_info_from_bug_list,
    extract_deployment_env,
)
from .functional.chart_generator import generate_all_charts
from .functional.word_generator import generate_report as generate_word_report
from .functional.markdown_generator import generate_markdown_report
from .functional.config import (
    get_report_filename,
    REPORT_FORMAT_DOCX,
    REPORT_FORMAT_MD,
)


logger = logging.getLogger(__name__)


class FunctionalReportService:
    """
    功能测试报告服务

    提供完整的报告生成流程：上传Excel → 分析数据 → 生成图表 → 生成报告
    """

    def __init__(self, report_base_dir: str):
        """
        Args:
            report_base_dir: 报告存储基础目录（如 data/reports/functional/）
        """
        self._base_dir = Path(report_base_dir)
        self._base_dir.mkdir(parents=True, exist_ok=True)

    # ==================== 公共方法 ====================

    def save_uploaded_file(self, file_content: bytes, filename: str, session_id: str) -> str:
        """
        保存上传的Excel文件到会话目录

        Args:
            file_content: 文件二进制内容
            filename: 文件名
            session_id: 会话ID

        Returns:
            保存的文件路径
        """
        session_dir = self._get_session_dir(session_id)
        file_path = session_dir / filename
        file_path.write_bytes(file_content)
        logger.info(f"文件已保存: {file_path}")
        return str(file_path)

    def analyze_and_generate(
        self,
        testcase_path: str,
        buglist_path: str,
        project_name: str,
        report_format: str = "docx",
        session_id: str = "",
    ) -> Dict:
        """
        执行完整分析并生成报告

        Args:
            testcase_path: 测试用例Excel文件路径
            buglist_path: BUG列表Excel文件路径
            project_name: 项目名称
            report_format: 报告格式（docx/md/both）
            session_id: 会话ID（沿用上传的session，保证报告与上传文件在同一目录）

        Returns:
            {
                "session_id": str,
                "project_name": str,
                "case_analysis": dict,
                "bug_analysis": dict,
                "case_bug_link_count": int,
                "charts": dict,
                "reports": list[str],
            }
        """
        # 1. 读取Excel数据
        logger.info("步骤1: 读取测试数据...")
        test_cases = read_test_cases(testcase_path)
        logger.info(f"  [OK] 读取到 {len(test_cases)} 条测试用例")

        bugs = read_bug_list(buglist_path)
        logger.info(f"  [OK] 读取到 {len(bugs)} 条BUG记录")

        if not project_name and bugs:
            project_name = bugs[0].project_name
        if not project_name:
            project_name = "未命名项目"

        # 2. 分析数据
        logger.info("步骤2: 分析测试数据...")
        case_analysis = analyze_test_cases(test_cases)
        bug_analysis = analyze_bugs(bugs)
        case_bug_link = link_bugs_to_cases(bugs, test_cases)
        logger.info(f"  [OK] 用例数: {case_analysis.total_cases}, 通过率: {case_analysis.pass_rate}%")
        logger.info(f"  [OK] BUG总数: {bug_analysis.total_bugs}, 遗留: {bug_analysis.remaining_bugs}")
        logger.info(f"  [OK] 关联了 {len(case_bug_link)} 个用例到BUG")

        # 3. 生成图表
        session_id = session_id or self._generate_session_id()
        charts_dir = self._get_charts_dir(session_id)
        logger.info("步骤3: 生成统计图表...")
        chart_paths = generate_all_charts(case_analysis, bug_analysis, str(charts_dir))
        logger.info(f"  [OK] 生成了 {len(chart_paths)} 个图表")

        # 4. 生成报告
        reports_dir = self._get_reports_dir(session_id)
        formats_to_generate = ["docx", "md"] if report_format == "both" else [report_format]
        generated_reports = []

        for fmt in formats_to_generate:
            logger.info(f"步骤4: 生成{fmt.upper()}报告...")
            output_file = str(reports_dir / get_report_filename(project_name, format=fmt))

            if fmt == "docx":
                report_path = generate_word_report(
                    template_path="",
                    case_analysis=case_analysis,
                    bug_analysis=bug_analysis,
                    case_bug_link=case_bug_link,
                    chart_paths=chart_paths,
                    output_path=output_file,
                    project_name=project_name,
                    cases=test_cases,
                    bugs=bugs,
                )
            else:
                report_path = generate_markdown_report(
                    template_path="",
                    case_analysis=case_analysis,
                    bug_analysis=bug_analysis,
                    case_bug_link=case_bug_link,
                    chart_paths=chart_paths,
                    output_path=output_file,
                    project_name=project_name,
                    cases=test_cases,
                    bugs=bugs,
                )
            generated_reports.append(report_path)
            logger.info(f"  [OK] {fmt.upper()}报告已生成: {report_path}")

        return {
            "session_id": session_id,
            "project_name": project_name,
            "case_analysis": self._case_analysis_to_dict(case_analysis),
            "bug_analysis": self._bug_analysis_to_dict(bug_analysis),
            "case_bug_link_count": len(case_bug_link),
            "charts": chart_paths,
            "reports": generated_reports,
        }

    def get_report_info(self, session_id: str) -> Dict:
        """
        获取会话的报告信息

        Args:
            session_id: 会话ID

        Returns:
            报告信息字典
        """
        session_dir = self._get_session_dir(session_id)
        reports_dir = session_dir / "reports"
        charts_dir = session_dir / "charts"

        reports = []
        if reports_dir.exists():
            for f in sorted(reports_dir.iterdir()):
                if f.is_file():
                    reports.append({
                        "name": f.name,
                        "path": str(f),
                        "size": f.stat().st_size,
                    })

        charts = []
        if charts_dir.exists():
            for f in sorted(charts_dir.iterdir()):
                if f.is_file():
                    charts.append({
                        "name": f.name,
                        "path": str(f),
                    })

        # 读取分析结果缓存
        analysis_path = session_dir / "analysis_result.json"
        analysis_result = {}
        if analysis_path.exists():
            import json
            try:
                analysis_result = json.loads(analysis_path.read_text(encoding="utf-8"))
            except Exception:
                pass

        return {
            "session_id": session_id,
            "reports": reports,
            "charts": charts,
            "analysis": analysis_result,
        }

    # ==================== 内部方法 ====================

    def _get_session_dir(self, session_id: str) -> Path:
        """获取会话目录"""
        session_dir = self._base_dir / session_id
        session_dir.mkdir(parents=True, exist_ok=True)
        return session_dir

    def _get_charts_dir(self, session_id: str) -> Path:
        """获取图表目录"""
        charts_dir = self._get_session_dir(session_id) / "charts"
        charts_dir.mkdir(parents=True, exist_ok=True)
        return charts_dir

    def _get_reports_dir(self, session_id: str) -> Path:
        """获取报告目录"""
        reports_dir = self._get_session_dir(session_id) / "reports"
        reports_dir.mkdir(parents=True, exist_ok=True)
        return reports_dir

    def _generate_session_id(self) -> str:
        """生成唯一会话ID"""
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        short_uuid = uuid.uuid4().hex[:8]
        return f"func_{timestamp}_{short_uuid}"

    @staticmethod
    def _case_analysis_to_dict(analysis: TestCaseAnalysis) -> Dict:
        """将TestCaseAnalysis转为可序列化字典"""
        module_stats = {}
        for name, stat in analysis.module_stats.items():
            module_stats[name] = {
                "module_name": stat.module_name,
                "total_cases": stat.total_cases,
                "passed_cases": stat.passed_cases,
                "failed_cases": stat.failed_cases,
                "unverified_cases": stat.unverified_cases,
                "undeveloped_cases": stat.undeveloped_cases,
                "pass_rate": stat.pass_rate,
            }

        return {
            "total_cases": analysis.total_cases,
            "executed_cases": analysis.executed_cases,
            "passed_cases": analysis.passed_cases,
            "failed_cases": analysis.failed_cases,
            "unverified_cases": analysis.unverified_cases,
            "undeveloped_cases": analysis.undeveloped_cases,
            "pass_rate": analysis.pass_rate,
            "execution_rate": analysis.execution_rate,
            "module_stats": module_stats,
        }

    @staticmethod
    def _bug_analysis_to_dict(analysis: BugAnalysis) -> Dict:
        """将BugAnalysis转为可序列化字典"""
        return {
            "total_bugs": analysis.total_bugs,
            "remaining_bugs": analysis.remaining_bugs,
            "closed_bugs": analysis.closed_bugs,
            "level_distribution": analysis.level_distribution,
            "status_distribution": analysis.status_distribution,
            "type_distribution": analysis.type_distribution,
            "module_distribution": analysis.module_distribution,
            "remaining_by_level": analysis.remaining_by_level,
        }