#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：markdown_generator.py
模块描述：功能测试报告 - Markdown报告生成模块，生成10章节功能测试报告

移植自独立工具 FunctionalTestReportGeneration/src/markdown_generator.py，
去除 TEMPLATE_FILE 依赖（输出路径由调用方传入），
修复"未执行用例数量"统计 bug（unverified+undeveloped），import 改为相对导入。

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

from pathlib import Path
from typing import Dict, List, Any, Optional
from datetime import datetime

from .data_analyzer import TestCaseAnalysis, BugAnalysis, ModuleStat
from .excel_reader import TestCase, Bug
from .data_extractor import (
    extract_testers,
    extract_test_lead,
    extract_server_info,
    extract_version_info_from_bug_list,
    extract_deployment_env,
)
from .config import (
    DATE_FORMAT,
    get_report_number,
    # 测试结果枚举
    TEST_RESULT_PASS,
    TEST_RESULT_FAIL,
    TEST_RESULT_UNVERIFIED,
    TEST_RESULT_UNDEVELOPED,
    # BUG等级枚举
    BUG_LEVEL_1,
    BUG_LEVEL_2,
    BUG_LEVEL_3,
    BUG_LEVEL_4,
    # BUG状态枚举
    BUG_STATUS_ACTIVE,
    BUG_STATUS_RESOLVED,
    BUG_STATUS_CLOSED,
    REMAINING_BUG_STATUS,
    # BUG等级描述
    BUG_LEVEL_DESCRIPTION,
    # 自动填充默认值
    DEFAULT_TEST_LEAD,
    DEFAULT_TESTER,
    DEFAULT_TEST_ENV,
    DEFAULT_SYSTEM_VERSION,
    DEFAULT_SERVER_ADDRESS,
    DEFAULT_DEPLOYMENT_ENV,
    # 模板配置工具函数
    get_default_config,
    merge_template_config,
)


def render_markdown_table(headers: List[str], rows: List[List[str]]) -> str:
    """
    渲染Markdown表格

    Args:
        headers: 表头列表
        rows: 数据行列表

    Returns:
        Markdown表格字符串
    """
    if not rows:
        return "*无数据*"

    lines = []

    # 表头
    lines.append("| " + " | ".join(headers) + " |")

    # 分隔线
    lines.append("| " + " | ".join(["---"] * len(headers)) + " |")

    # 数据行
    for row in rows:
        # 确保每行都有与表头相同数量的列
        while len(row) < len(headers):
            row.append("")
        # 只取前len(headers)列
        row = row[:len(headers)]
        lines.append("| " + " | ".join(str(cell) if cell is not None else "" for cell in row) + " |")

    return "\n".join(lines)

_ZH_NUM_MAP = {1: "一", 2: "二", 3: "三", 4: "四", 5: "五", 6: "六", 7: "七", 8: "八", 9: "九", 10: "十"}
def _zh_num(n: int) -> str:
    """整数转中文数字（1-10），超出返回阿拉伯数字"""
    return _ZH_NUM_MAP.get(n, str(n))
def _title(num: int, name: str, level: int = 2) -> str:
    """生成章节标题（序号动态生成中文数字）"""
    return f"{'#' * level} {_zh_num(num)}、{name}"




def generate_markdown_report(
    template_path: str,
    case_analysis: TestCaseAnalysis,
    bug_analysis: BugAnalysis,
    case_bug_link: Dict[str, List[Bug]],
    chart_paths: Dict[str, str],
    output_path: str,
    project_name: str,
    cases: List[TestCase],
    bugs: List[Bug],
    template_config: Optional[Dict] = None
) -> str:
    """
    生成Markdown格式功能测试报告

    Args:
        template_path: 模板文件路径（暂未使用，使用固定格式生成）
        case_analysis: 用例分析结果
        bug_analysis: BUG分析结果
        case_bug_link: 用例-BUG关联
        chart_paths: 图表文件路径字典
        output_path: 输出文件路径
        project_name: 项目名称
        cases: 测试用例列表
        bugs: BUG列表
        template_config: 模板配置（可选），None 时使用默认值（章节全开+硬编码默认值）

    Returns:
        生成的报告文件路径
    """
    # 合并模板配置
    cfg = merge_template_config(get_default_config(), template_config)
    chapters = cfg.get("chapters", {})
    defaults = cfg.get("defaults", {})

    # 章节动态序号计数器（仅对启用章节自增）
    ch_idx = 0
    # 获取当前日期
    current_date = datetime.now().strftime(DATE_FORMAT)
    report_number = get_report_number(project_name, current_date)

    # 开始构建Markdown内容
    content_parts = []

    # ==================== 报告标题 ====================
    content_parts.append(f"# {project_name}功能测试报告\n")

    # ==================== 一、报告基本信息 ====================
    if chapters.get("report_basic_info", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "报告基本信息", level=2))
        content_parts.append(f"### {ch_idx}.1 报告标识\n")

        headers = ["项目", "内容"]
        rows = [
            ["报告编号", report_number],
            ["报告标题", f"{project_name}功能测试报告"],
            ["报告版本", "v1.0"],
            ["生成日期", current_date],
            ["报告状态", "已生成"],
        ]
        content_parts.append(render_markdown_table(headers, rows) + "\n")

        content_parts.append(f"### {ch_idx}.2 测试概览\n")
        headers = ["项目", "内容"]
        rows = [
            ["项目名称", project_name],
            ["测试负责人", extract_test_lead(bugs) if bugs else defaults.get("test_lead", DEFAULT_TEST_LEAD)],
            ["测试人员", extract_testers(bugs) if bugs else defaults.get("tester", DEFAULT_TESTER)],
            ["测试环境", extract_server_info(cases) if cases else defaults.get("test_env", DEFAULT_TEST_ENV)],
            ["测试类型", "功能测试"],
        ]
        content_parts.append(render_markdown_table(headers, rows) + "\n")

        content_parts.append(f"### {ch_idx}.3 被测系统信息\n")
        headers = ["项目", "内容"]
        # 提取自动填充数据
        system_version = extract_version_info_from_bug_list(bugs) if bugs else defaults.get("system_version", DEFAULT_SYSTEM_VERSION)
        server_address = extract_server_info(cases) if cases else defaults.get("server_address", DEFAULT_SERVER_ADDRESS)
        deployment_env = extract_deployment_env(cases) if cases else defaults.get("deployment_env", DEFAULT_DEPLOYMENT_ENV)

        # Markdown支持多行显示，将版本信息格式化为代码块
        if "\n" in system_version:
            system_version_display = f"```\n{system_version}\n```"
        else:
            system_version_display = system_version

        rows = [
            ["系统名称", project_name],
            ["系统版本", system_version_display],
            ["服务器地址", server_address],
            ["部署环境", deployment_env],
        ]
        content_parts.append(render_markdown_table(headers, rows) + "\n")

    # ==================== 二、测试执行摘要 ====================
    if chapters.get("execution_summary", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "测试执行摘要", level=2))

        content_parts.append(f"### {ch_idx}.1 测试结果统计\n")
        total = case_analysis.total_cases
        headers = ["统计项", "数量", "占比"]
        unexecuted = case_analysis.unverified_cases + case_analysis.undeveloped_cases
        rows = [
            ["测试用例总数", str(total), "100%"],
            ["执行用例数量", str(case_analysis.executed_cases), f"{case_analysis.execution_rate}%"],
            ["通过用例数量", str(case_analysis.passed_cases), f"{case_analysis.pass_rate}%"],
            ["失败用例数量", str(case_analysis.failed_cases), f"{round(case_analysis.failed_cases / total * 100, 2)}%" if total > 0 else "0%"],
            ["未执行用例数量", str(unexecuted),
             f"{round(unexecuted / total * 100, 2)}%" if total > 0 else "0%"],
        ]
        content_parts.append(render_markdown_table(headers, rows) + "\n")

        content_parts.append(f"### {ch_idx}.2 BUG统计汇总\n")
        total_bugs = bug_analysis.total_bugs
        headers = ["BUG等级", "数量", "占比", "已关闭", "未关闭"]
        rows = []
        for level in [BUG_LEVEL_1, BUG_LEVEL_2, BUG_LEVEL_3, BUG_LEVEL_4]:
            count = bug_analysis.level_distribution.get(level, 0)
            percentage = round(count / total_bugs * 100, 2) if total_bugs > 0 else 0
            # 计算该等级的未关闭数量
            remaining = sum(
                1 for bug in bugs
                if bug.bug_level == level and bug.bug_status in REMAINING_BUG_STATUS
            )
            closed = count - remaining
            rows.append([
                f"{BUG_LEVEL_DESCRIPTION[level]}({level})",
                str(count),
                f"{percentage}%",
                str(closed),
                str(remaining)
            ])
        content_parts.append(render_markdown_table(headers, rows) + "\n")

        content_parts.append(f"### {ch_idx}.3 测试结论\n")
        if bug_analysis.remaining_bugs == 0 and case_analysis.failed_cases == 0:
            conclusion = "✓ **测试通过** - 系统功能正常，无致命/严重缺陷，可以发布"
        elif case_analysis.failed_cases > 0:
            conclusion = "✗ **测试不通过** - 存在失败的测试用例，不建议发布"
        elif bug_analysis.remaining_bugs > 0:
            critical = bug_analysis.remaining_by_level.get(BUG_LEVEL_1, 0) + \
                       bug_analysis.remaining_by_level.get(BUG_LEVEL_2, 0)
            if critical > 0:
                conclusion = "✗ **测试不通过** - 存在阻塞性缺陷，不建议发布"
            else:
                conclusion = "⚠ **有条件通过** - 存在非阻塞性缺陷，经评估可以发布"
        else:
            conclusion = "待评估"
        content_parts.append(f"{conclusion}\n")

    # ==================== 三、测试范围 ====================
    if chapters.get("test_scope", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "测试范围", level=2))

        content_parts.append(f"### {ch_idx}.1 测试模块覆盖\n")
        headers = ["序号", "功能模块", "用例数量", "通过", "失败", "未执行", "覆盖率"]
        module_rows = []
        for i, (module_name, stat) in enumerate(case_analysis.module_stats.items(), 1):
            module_rows.append([
                str(i),
                module_name,
                str(stat.total_cases),
                str(stat.passed_cases),
                str(stat.failed_cases),
                str(stat.unverified_cases + stat.undeveloped_cases),
                f"{stat.pass_rate}%"
            ])
        content_parts.append(render_markdown_table(headers, module_rows) + "\n")

    # ==================== 四、测试用例执行详情 ====================
    if chapters.get("case_details", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "测试用例执行详情", level=2))

        # 通过用例列表
        content_parts.append(f"### {ch_idx}.1 通过用例列表\n")
        passed_cases = [c for c in cases if c.test_result == TEST_RESULT_PASS]
        if passed_cases:
            headers = ["序号", "用例编号", "功能模块", "用例名称", "用例等级", "测试结果"]
            rows = []
            for i, case in enumerate(passed_cases[:50], 1):  # 限制显示50条
                rows.append([
                    str(i),
                    case.case_number,
                    case.module,
                    case.name[:50],  # 限制长度
                    str(case.level),
                    case.test_result
                ])
            if len(passed_cases) > 50:
                rows.append(["...", f"等{len(passed_cases)}条通过用例", "", "", "", ""])
            content_parts.append(render_markdown_table(headers, rows) + "\n")
        else:
            content_parts.append("*无通过用例*\n")

        # 失败用例列表
        content_parts.append(f"### {ch_idx}.2 失败用例列表\n")
        failed_cases = [c for c in cases if c.test_result == TEST_RESULT_FAIL]
        if failed_cases:
            headers = ["序号", "用例编号", "功能模块", "用例名称", "用例等级", "关联BUG"]
            rows = []
            for i, case in enumerate(failed_cases, 1):
                related_bugs = case_bug_link.get(case.case_number, [])
                bug_str = "; ".join([b.bug_name[:30] for b in related_bugs[:3]])  # 限制显示
                if len(related_bugs) > 3:
                    bug_str += f" 等{len(related_bugs)}个"
                rows.append([
                    str(i),
                    case.case_number,
                    case.module,
                    case.name[:50],
                    str(case.level),
                    bug_str if bug_str else "无"
                ])
            content_parts.append(render_markdown_table(headers, rows) + "\n")
        else:
            content_parts.append("*无失败用例*\n")

        # 未执行用例列表
        content_parts.append(f"### {ch_idx}.3 未执行用例列表\n")
        unexecuted_cases = [c for c in cases if c.test_result in [TEST_RESULT_UNVERIFIED, TEST_RESULT_UNDEVELOPED]]
        if unexecuted_cases:
            headers = ["序号", "用例编号", "功能模块", "用例名称", "用例等级", "未执行原因"]
            rows = []
            for i, case in enumerate(unexecuted_cases[:20], 1):  # 限制显示20条
                reason = "未验证" if case.test_result == TEST_RESULT_UNVERIFIED else "未开发"
                rows.append([
                    str(i),
                    case.case_number,
                    case.module,
                    case.name[:50],
                    str(case.level),
                    reason
                ])
            if len(unexecuted_cases) > 20:
                rows.append(["...", f"等{len(unexecuted_cases)}条未执行用例", "", "", "", ""])
            content_parts.append(render_markdown_table(headers, rows) + "\n")
        else:
            content_parts.append("*无未执行用例*\n")

    # ==================== 五、BUG详细列表 ====================
    if chapters.get("bug_list", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "BUG详细列表", level=2))

        content_parts.append(f"### {ch_idx}.1 BUG详细记录\n")
        if bugs:
            headers = ["序号", "BUG等级", "BUG状态", "BUG名称", "BUG类型", "提单人员", "负责人员"]
            rows = []
            for i, bug in enumerate(bugs, 1):
                rows.append([
                    str(i),
                    bug.bug_level,
                    bug.bug_status,
                    bug.bug_name[:50],
                    bug.bug_type,
                    bug.reporter,
                    bug.owner
                ])
            content_parts.append(render_markdown_table(headers, rows) + "\n")
        else:
            content_parts.append("*无BUG记录*\n")

        content_parts.append(f"### {ch_idx}.2 遗留BUG说明\n")
        remaining_bugs = [b for b in bugs if b.bug_status in REMAINING_BUG_STATUS]
        if remaining_bugs:
            headers = ["BUG编号", "BUG等级", "问题描述", "遗留原因", "后续计划"]
            rows = []
            for bug in remaining_bugs:
                rows.append([
                    bug.bug_name[:30],
                    bug.bug_level,
                    bug.bug_name[:60],
                    "待修复",
                    "后续版本修复"
                ])
            content_parts.append(render_markdown_table(headers, rows) + "\n")
        else:
            content_parts.append("*无遗留BUG*\n")

    # ==================== 六、图表分析 ====================
    if chapters.get("chart_analysis", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "图表分析", level=2))

        # 添加图表
        if "bug_level" in chart_paths:
            content_parts.append(f"### {ch_idx}.1 BUG等级分布\n")
            # 获取图表相对路径
            chart_rel_path = _get_relative_chart_path(chart_paths["bug_level"], output_path)
            content_parts.append(f"![BUG等级分布]({chart_rel_path})\n")

        if "test_result" in chart_paths:
            content_parts.append(f"### {ch_idx}.2 用例执行结果分布\n")
            chart_rel_path = _get_relative_chart_path(chart_paths["test_result"], output_path)
            content_parts.append(f"![用例执行结果分布]({chart_rel_path})\n")

        if "pass_rate" in chart_paths:
            content_parts.append(f"### {ch_idx}.3 用例执行情况统计\n")
            chart_rel_path = _get_relative_chart_path(chart_paths["pass_rate"], output_path)
            content_parts.append(f"![用例执行情况统计]({chart_rel_path})\n")

        if "bug_by_module" in chart_paths:
            content_parts.append(f"### {ch_idx}.4 各模块BUG分布\n")
            chart_rel_path = _get_relative_chart_path(chart_paths["bug_by_module"], output_path)
            content_parts.append(f"![各模块BUG分布]({chart_rel_path})\n")

    # ==================== 七、测试执行记录 ====================
    if chapters.get("execution_records", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "测试执行记录", level=2))

        content_parts.append(f"### {ch_idx}.1 执行时间统计\n")
        headers = ["统计项", "时间"]
        rows = [
            ["测试开始时间", current_date],
            ["测试结束时间", current_date],
            ["测试总耗时", "自动化生成"],
            ["平均用例执行时间", "N/A"]
        ]
        content_parts.append(render_markdown_table(headers, rows) + "\n")

        content_parts.append(f"### {ch_idx}.2 执行人员分工\n")
        headers = ["测试人员", "负责模块", "执行用例数", "发现BUG数"]
        rows = [
            ["自动化工具", "全模块", str(case_analysis.total_cases), str(bug_analysis.total_bugs)]
        ]
        content_parts.append(render_markdown_table(headers, rows) + "\n")

    # ==================== 八、测试附件 ====================
    if chapters.get("attachments", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "测试附件", level=2))

        content_parts.append(f"### {ch_idx}.1 测试截图\n")
        content_parts.append("*无测试截图*\n")

        content_parts.append(f"### {ch_idx}.2 测试日志\n")
        content_parts.append("*无测试日志*\n")

    # ==================== 九、风险评估与建议 ====================
    if chapters.get("risk_assessment", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "风险评估与建议", level=2))

        content_parts.append(f"### {ch_idx}.1 质量风险分析\n")
        content_parts.append("*无特别风险*\n")

        content_parts.append(f"### {ch_idx}.2 发布建议\n")
        # 根据测试结果自动生成发布建议
        if case_analysis.failed_cases == 0 and bug_analysis.remaining_bugs == 0:
            recommendation = "- [x] **建议发布** - 质量达标，风险可控"
        elif case_analysis.failed_cases > 0:
            recommendation = "- [ ] **建议延期** - 存在失败的测试用例，需要修复"
        elif bug_analysis.remaining_bugs > 0:
            critical = bug_analysis.remaining_by_level.get(BUG_LEVEL_1, 0) + \
                       bug_analysis.remaining_by_level.get(BUG_LEVEL_2, 0)
            if critical > 0:
                recommendation = "- [ ] **建议延期** - 存在重大缺陷，需要修复"
            else:
                recommendation = "- [ ] **有条件发布** - 存在已知问题，需制定规避方案"
        else:
            recommendation = "- [x] **建议发布** - 质量达标，风险可控"
        content_parts.append(recommendation + "\n")

        content_parts.append(f"### {ch_idx}.3 改进建议\n")
        content_parts.append("待补充\n")

    # ==================== 十、附录 ====================
    if chapters.get("appendix", True):
        ch_idx += 1
        content_parts.append(_title(ch_idx, "附录", level=2))

        content_parts.append(f"### {ch_idx}.1 用例与BUG关联表\n")
        headers = ["用例编号", "用例名称", "关联BUG编号", "BUG状态"]
        rows = []
        for case_number, bug_list in case_bug_link.items():
            if bug_list:
                for bug in bug_list[:3]:  # 每个用例限制显示3个BUG
                    rows.append([
                        case_number,
                        "关联用例",
                        bug.bug_name[:30],
                        bug.bug_status
                    ])
        if rows:
            content_parts.append(render_markdown_table(headers, rows) + "\n")
        else:
            content_parts.append("*无关联数据*\n")

        content_parts.append(f"### {ch_idx}.2 术语表\n")
        content_parts.append("*无术语定义*\n")

        content_parts.append(f"### {ch_idx}.3 参考文档\n")
        content_parts.append("*无参考文档*\n")

        content_parts.append(f"### {ch_idx}.4 修订历史\n")
        headers = ["版本", "修订日期", "修订人", "修订内容"]
        rows = [
            ["v1.0", current_date, "自动化工具", "自动生成初始报告"]
        ]
        content_parts.append(render_markdown_table(headers, rows) + "\n")

    # 合并所有内容
    content = "\n".join(content_parts)

    # 确保输出目录存在
    output_dir = Path(output_path).parent
    output_dir.mkdir(parents=True, exist_ok=True)

    # 保存Markdown文件
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(content)

    return output_path


def _get_relative_chart_path(chart_abs_path: str, report_path: str) -> str:
    """
    获取图表相对于报告文件的路径

    Args:
        chart_abs_path: 图表的绝对路径
        report_path: 报告文件的路径

    Returns:
        相对路径
    """
    try:
        chart_path = Path(chart_abs_path)
        report_dir = Path(report_path).parent

        # 尝试获取相对路径
        try:
            rel_path = chart_path.relative_to(report_dir)
            return str(rel_path).replace("\\", "/")
        except ValueError:
            # 如果不在相对路径内，使用绝对路径
            return chart_abs_path
    except Exception:
        return chart_abs_path


def fill_template(template_path: str, data: Dict[str, Any]) -> str:
    """
    填充模板内容

    Args:
        template_path: 模板文件路径
        data: 填充数据

    Returns:
        填充后的内容
    """
    # 读取模板文件
    template_file = Path(template_path)
    if not template_file.exists():
        raise FileNotFoundError(f"模板文件不存在: {template_path}")

    with open(template_path, 'r', encoding='utf-8') as f:
        content = f.read()

    # 替换占位符
    for key, value in data.items():
        placeholder = f"{{{key}}}"
        content = content.replace(placeholder, str(value))

    return content