#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：chart_generator.py
模块描述：功能测试报告 - 图表生成模块，使用matplotlib生成统计图表

移植自独立工具 FunctionalTestReportGeneration/src/chart_generator.py，
去除 TEMP_DIR 依赖（输出路径由调用方传入），import 改为相对导入。

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

import matplotlib
matplotlib.use('Agg')  # 使用非交互式后端
import matplotlib.pyplot as plt
from pathlib import Path
from typing import Dict, List, Tuple, Optional
import os

from .data_analyzer import TestCaseAnalysis, BugAnalysis
from .config import (
    CHART_FORMAT,
    CHART_DPI,
    CHART_FIGURE_SIZE,
    CHINESE_FONT,
    CHART_COLORS,
    # 测试结果枚举
    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_LEVEL_DESCRIPTION,
    # 模板配置工具函数
    get_default_config,
    merge_template_config,
)


def _setup_chinese_font(chinese_font: str = None) -> None:
    """
    配置中文字体

    使用 matplotlib.font_manager 主动查找系统中已有的中文字体，
    并将字体文件路径显式注册到 fontManager，避免因字体缓存导致的中文乱码。

    Args:
        chinese_font: 优先使用的中文字体名称（来自模板配置），None 时使用默认候选列表
    """
    import matplotlib.font_manager as fm

    # 常见中文字体候选名称
    candidate_names = [
        # Windows
        "Microsoft YaHei", "SimHei", "SimSun", "KaiTi",
        # Linux
        "WenQuanYi Micro Hei", "WenQuanYi Zen Hei",
        "Noto Sans CJK SC", "Noto Sans CJK JP", "Droid Sans Fallback",
        # macOS
        "PingFang SC", "Heiti SC", "STHeiti",
    ]

    # 如果模板配置指定了中文字体，优先使用
    if chinese_font:
        candidate_names.insert(0, chinese_font)

    # 常见中文字体文件路径（用于 addfont 显式注册）
    candidate_paths = [
        "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
        "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
        "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
        "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",
        "C:/Windows/Fonts/msyh.ttc",
        "C:/Windows/Fonts/simhei.ttf",
    ]

    # 1. 显式注册字体文件（若存在）
    for path in candidate_paths:
        if os.path.exists(path):
            try:
                fm.fontManager.addfont(path)
            except Exception:
                pass

    # 刷新字体管理器，确保新注册的字体被识别
    try:
        fm._load_fontmanager(try_read_cache=False)
    except Exception:
        pass

    # 2. 从 fontManager 中筛选已可用的中文字体
    available = set()
    for font in fm.fontManager.ttflist:
        name_lower = font.name.lower()
        if any(k in name_lower for k in [
            "yahei", "simhei", "simsun", "kaiti", "wenquan", "wqy",
            "noto", "droid", "pingfang", "heiti", "cjk", "fang", "song",
        ]):
            available.add(font.name)

    # 3. 按候选顺序选择第一个可用字体
    selected = next((n for n in candidate_names if n in available), None)
    if selected:
        plt.rcParams['font.sans-serif'] = [selected]
    else:
        # 兜底：使用默认配置
        plt.rcParams['font.sans-serif'] = ['DejaVu Sans']

    # 解决负号显示问题
    plt.rcParams['axes.unicode_minus'] = False


def _get_value_with_percentage(value: int, total: int) -> str:
    """
    获取带百分比的值标签

    Args:
        value: 数值
        total: 总数

    Returns:
        带百分比的字符串（如："10 (20%)"）
    """
    if total == 0:
        return f"{value} (0%)"

    percentage = round(value / total * 100, 1)
    return f"{value} ({percentage}%)"


def generate_bug_level_chart(
    analysis: BugAnalysis,
    output_path: str = None,
    chart_style: dict = None
) -> str:
    """
    生成BUG等级分布图（饼图）

    Args:
        analysis: BUG分析结果
        output_path: 输出文件路径（可选，默认使用临时目录）
        chart_style: 图表样式配置（来自模板配置），None 时使用默认值

    Returns:
        图片文件路径
    """
    # 读取配置
    cs = chart_style or {}
    _dpi = cs.get("dpi", CHART_DPI)
    _fig_w = cs.get("figure_width", CHART_FIGURE_SIZE[0])
    _fig_h = cs.get("figure_height", CHART_FIGURE_SIZE[1])
    _font = cs.get("chinese_font", CHINESE_FONT)
    _colors = cs.get("colors", CHART_COLORS)

    # 配置中文字体
    _setup_chinese_font(_font)

    # 准备数据
    levels = [BUG_LEVEL_1, BUG_LEVEL_2, BUG_LEVEL_3, BUG_LEVEL_4]
    values = [
        analysis.level_distribution.get(BUG_LEVEL_1, 0),
        analysis.level_distribution.get(BUG_LEVEL_2, 0),
        analysis.level_distribution.get(BUG_LEVEL_3, 0),
        analysis.level_distribution.get(BUG_LEVEL_4, 0),
    ]

    labels = [
        f"{BUG_LEVEL_DESCRIPTION[BUG_LEVEL_1]} ({BUG_LEVEL_1})",
        f"{BUG_LEVEL_DESCRIPTION[BUG_LEVEL_2]} ({BUG_LEVEL_2})",
        f"{BUG_LEVEL_DESCRIPTION[BUG_LEVEL_3]} ({BUG_LEVEL_3})",
        f"{BUG_LEVEL_DESCRIPTION[BUG_LEVEL_4]} ({BUG_LEVEL_4})",
    ]

    # 过滤掉值为0的数据
    filtered_data = [(label, value) for label, value in zip(labels, values) if value > 0]
    if not filtered_data:
        labels = ["无BUG"]
        values = [1]
    else:
        labels, values = zip(*filtered_data)

    # 创建图表
    fig, ax = plt.subplots(figsize=(_fig_w, _fig_h))

    # 颜色
    colors = [
        _colors["bug_1"],
        _colors["bug_2"],
        _colors["bug_3"],
        _colors["bug_4"],
    ]

    # 绘制饼图
    wedges, texts, autotexts = ax.pie(
        values,
        labels=labels,
        colors=colors[:len(values)],
        autopct='%1.1f%%',
        startangle=90,
        textprops={'fontsize': 12}
    )

    # 设置百分比文字样式
    for autotext in autotexts:
        autotext.set_color('white')
        autotext.set_fontweight('bold')

    # 设置标题
    ax.set_title('BUG等级分布', fontsize=16, fontweight='bold', pad=20)

    # 确保饼图是圆形
    ax.axis('equal')

    # 保存图片
    if output_path is None:
        output_path = f"bug_level_chart{CHART_FORMAT}"

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

    plt.savefig(output_path, dpi=_dpi, bbox_inches='tight')
    plt.close()

    return str(output_path)


def generate_test_result_chart(
    analysis: TestCaseAnalysis,
    output_path: str = None,
    chart_style: dict = None
) -> str:
    """
    生成用例执行结果分布图（饼图）

    Args:
        analysis: 用例分析结果
        output_path: 输出文件路径（可选，默认使用临时目录）
        chart_style: 图表样式配置（来自模板配置），None 时使用默认值

    Returns:
        图片文件路径
    """
    # 读取配置
    cs = chart_style or {}
    _dpi = cs.get("dpi", CHART_DPI)
    _fig_w = cs.get("figure_width", CHART_FIGURE_SIZE[0])
    _fig_h = cs.get("figure_height", CHART_FIGURE_SIZE[1])
    _font = cs.get("chinese_font", CHINESE_FONT)
    _colors = cs.get("colors", CHART_COLORS)

    # 配置中文字体
    _setup_chinese_font(_font)

    # 准备数据
    results = [
        TEST_RESULT_PASS,
        TEST_RESULT_FAIL,
        TEST_RESULT_UNVERIFIED,
        TEST_RESULT_UNDEVELOPED,
    ]

    values = [
        analysis.passed_cases,
        analysis.failed_cases,
        analysis.unverified_cases,
        analysis.undeveloped_cases,
    ]

    labels = [
        f"{TEST_RESULT_PASS}",
        f"{TEST_RESULT_FAIL}",
        f"{TEST_RESULT_UNVERIFIED}",
        f"{TEST_RESULT_UNDEVELOPED}",
    ]

    # 过滤掉值为0的数据
    filtered_data = [(label, value) for label, value in zip(labels, values) if value > 0]
    if not filtered_data:
        labels = ["无测试数据"]
        values = [1]
    else:
        labels, values = zip(*filtered_data)

    # 创建图表
    fig, ax = plt.subplots(figsize=(_fig_w, _fig_h))

    # 颜色
    colors = [
        _colors["pass"],
        _colors["fail"],
        _colors["unverified"],
        _colors["undeveloped"],
    ]

    # 绘制饼图
    wedges, texts, autotexts = ax.pie(
        values,
        labels=labels,
        colors=colors[:len(values)],
        autopct='%1.1f%%',
        startangle=90,
        textprops={'fontsize': 12}
    )

    # 设置百分比文字样式
    for autotext in autotexts:
        autotext.set_color('white')
        autotext.set_fontweight('bold')

    # 设置标题
    ax.set_title('用例执行结果分布', fontsize=16, fontweight='bold', pad=20)

    # 确保饼图是圆形
    ax.axis('equal')

    # 保存图片
    if output_path is None:
        output_path = f"test_result_chart{CHART_FORMAT}"

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

    plt.savefig(output_path, dpi=_dpi, bbox_inches='tight')
    plt.close()

    return str(output_path)


def generate_pass_rate_chart(
    analysis: TestCaseAnalysis,
    output_path: str = None,
    chart_style: dict = None
) -> str:
    """
    生成用例通过率图（柱状图）

    Args:
        analysis: 用例分析结果
        output_path: 输出文件路径（可选，默认使用临时目录）
        chart_style: 图表样式配置（来自模板配置），None 时使用默认值

    Returns:
        图片文件路径
    """
    # 读取配置
    cs = chart_style or {}
    _dpi = cs.get("dpi", CHART_DPI)
    _fig_w = cs.get("figure_width", CHART_FIGURE_SIZE[0])
    _fig_h = cs.get("figure_height", CHART_FIGURE_SIZE[1])
    _font = cs.get("chinese_font", CHINESE_FONT)
    _colors = cs.get("colors", CHART_COLORS)

    # 配置中文字体
    _setup_chinese_font(_font)

    # 准备数据
    categories = ['通过', '失败', '未验证', '未开发']
    values = [
        analysis.passed_cases,
        analysis.failed_cases,
        analysis.unverified_cases,
        analysis.undeveloped_cases,
    ]

    # 创建图表
    fig, ax = plt.subplots(figsize=(_fig_w, _fig_h))

    # 颜色
    colors = [
        _colors["pass"],
        _colors["fail"],
        _colors["unverified"],
        _colors["undeveloped"],
    ]

    # 绘制柱状图
    bars = ax.bar(categories, values, color=colors, edgecolor='black', linewidth=1.5)

    # 在柱子上显示数值
    for bar in bars:
        height = bar.get_height()
        if height > 0:
            ax.text(
                bar.get_x() + bar.get_width() / 2.,
                height,
                f'{int(height)}',
                ha='center',
                va='bottom',
                fontsize=12,
                fontweight='bold'
            )

    # 设置标题和标签
    ax.set_title('用例执行情况统计', fontsize=16, fontweight='bold', pad=20)
    ax.set_xlabel('测试结果', fontsize=12, fontweight='bold')
    ax.set_ylabel('用例数量', fontsize=12, fontweight='bold')

    # 显示网格
    ax.grid(axis='y', alpha=0.3, linestyle='--')

    # 添加通过率标注
    total = sum(values)
    if total > 0:
        pass_rate = analysis.pass_rate
        ax.text(
            0.98, 0.95,
            f'通过率: {pass_rate}%',
            transform=ax.transAxes,
            ha='right',
            va='top',
            fontsize=14,
            fontweight='bold',
            bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5)
        )

    # 保存图片
    if output_path is None:
        output_path = f"pass_rate_chart{CHART_FORMAT}"

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

    plt.savefig(output_path, dpi=_dpi, bbox_inches='tight')
    plt.close()

    return str(output_path)


def generate_bug_by_module_chart(
    analysis: BugAnalysis,
    output_path: str = None,
    chart_style: dict = None
) -> str:
    """
    生成按模块分布的BUG图（堆叠柱状图）

    Args:
        analysis: BUG分析结果
        output_path: 输出文件路径（可选，默认使用临时目录）
        chart_style: 图表样式配置（来自模板配置），None 时使用默认值

    Returns:
        图片文件路径
    """
    # 读取配置
    cs = chart_style or {}
    _dpi = cs.get("dpi", CHART_DPI)
    _font = cs.get("chinese_font", CHINESE_FONT)
    _colors = cs.get("colors", CHART_COLORS)

    # 配置中文字体
    _setup_chinese_font(_font)

    # 准备数据
    modules = list(analysis.module_distribution.keys())

    # 获取各模块的BUG数据
    level_1_data = []
    level_2_data = []
    level_3_data = []
    level_4_data = []

    for module in modules:
        dist = analysis.module_level_distribution.get(module, {})
        level_1_data.append(dist.get(BUG_LEVEL_1, 0))
        level_2_data.append(dist.get(BUG_LEVEL_2, 0))
        level_3_data.append(dist.get(BUG_LEVEL_3, 0))
        level_4_data.append(dist.get(BUG_LEVEL_4, 0))

    # 创建图表
    fig, ax = plt.subplots(figsize=(12, 6))

    # 绘制堆叠柱状图
    width = 0.6
    x = range(len(modules))

    ax.bar(x, level_1_data, width, label=f'{BUG_LEVEL_DESCRIPTION[BUG_LEVEL_1]}', color=_colors["bug_1"])
    ax.bar(x, level_2_data, width, bottom=level_1_data, label=f'{BUG_LEVEL_DESCRIPTION[BUG_LEVEL_2]}', color=_colors["bug_2"])

    # 计算第三层的底部（1级+2级）
    level_3_bottom = [level_1_data[i] + level_2_data[i] for i in range(len(modules))]
    ax.bar(x, level_3_data, width, bottom=level_3_bottom, label=f'{BUG_LEVEL_DESCRIPTION[BUG_LEVEL_3]}', color=_colors["bug_3"])

    # 计算第四层的底部（1级+2级+3级）
    level_4_bottom = [level_3_bottom[i] + level_3_data[i] for i in range(len(modules))]
    ax.bar(x, level_4_data, width, bottom=level_4_bottom, label=f'{BUG_LEVEL_DESCRIPTION[BUG_LEVEL_4]}', color=_colors["bug_4"])

    # 设置标题和标签
    ax.set_title('各模块BUG分布（按等级）', fontsize=16, fontweight='bold', pad=20)
    ax.set_xlabel('模块', fontsize=12, fontweight='bold')
    ax.set_ylabel('BUG数量', fontsize=12, fontweight='bold')

    # 设置x轴标签
    ax.set_xticks(x)
    ax.set_xticklabels(modules, rotation=45, ha='right')

    # 添加图例
    ax.legend(loc='upper right')

    # 显示网格
    ax.grid(axis='y', alpha=0.3, linestyle='--')

    plt.tight_layout()

    # 保存图片
    if output_path is None:
        output_path = f"bug_by_module_chart{CHART_FORMAT}"

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

    plt.savefig(output_path, dpi=_dpi, bbox_inches='tight')
    plt.close()

    return str(output_path)


def generate_all_charts(
    case_analysis: TestCaseAnalysis,
    bug_analysis: BugAnalysis,
    charts_dir: str,
    template_config: Optional[Dict] = None
) -> Dict[str, str]:
    """
    生成所有图表

    Args:
        case_analysis: 用例分析结果
        bug_analysis: BUG分析结果
        charts_dir: 图表输出目录
        template_config: 模板配置（可选），None 时使用默认值

    Returns:
        图表文件路径字典 {图表类型: 文件路径}
    """
    # 合并模板配置（图表样式）
    cfg = merge_template_config(get_default_config(), template_config)
    chart_style = cfg.get("chart_style", {})

    charts_dir = Path(charts_dir)
    charts_dir.mkdir(parents=True, exist_ok=True)
    charts = {}

    try:
        charts["bug_level"] = generate_bug_level_chart(
            bug_analysis, str(charts_dir / f"bug_level_chart{CHART_FORMAT}"), chart_style
        )
    except Exception as e:
        print(f"生成BUG等级分布图失败: {str(e)}")

    try:
        charts["test_result"] = generate_test_result_chart(
            case_analysis, str(charts_dir / f"test_result_chart{CHART_FORMAT}"), chart_style
        )
    except Exception as e:
        print(f"生成用例执行结果分布图失败: {str(e)}")

    try:
        charts["pass_rate"] = generate_pass_rate_chart(
            case_analysis, str(charts_dir / f"pass_rate_chart{CHART_FORMAT}"), chart_style
        )
    except Exception as e:
        print(f"生成用例通过率图失败: {str(e)}")

    try:
        charts["bug_by_module"] = generate_bug_by_module_chart(
            bug_analysis, str(charts_dir / f"bug_by_module_chart{CHART_FORMAT}"), chart_style
        )
    except Exception as e:
        print(f"生成各模块BUG分布图失败: {str(e)}")

    return charts