"""
互动组件数据导出

需求 20251123：
---------
在 PGsql数据库中  筛选数据
数据库相关配置  从.env中读取:
PG_DB_HOST = xxx
PG_DB_PORT = xxx
PG_DB_USER = xxx
PG_DB_PASSWORD = xxx
PG_DB_DATABASE = xxx

读取以下数据表:
user_component_play_record_0 ~ user_component_play_record_7

支持输入时间范围
起始时间 和 截止时间   配置格式:   "20250110"

数据表中的时间字段为 updated_at , 格式样例: "2025-11-05 19:35:46.698246+08:00"

在这些时间范围内，筛选以下字段数据  导出为excel文件:

c_type 与 c_id 非空

输出以下字段：
user_id,
session_id,
c_type,
c_id,
play_result,
user_behavior_info,
updated_at

写一个简单清晰的 数据导出脚本， 输入参数都直接在脚本开头定义和修改。 不要改动文件开头的需求描述，直接追加代码。
-------

需求二:
读取上述  输出的  excel 文件， 围绕 每个组件进行 统计，

统计方式如下:
仅计算 c_type 与 c_id 非空 的记录

以每个 c_type + c_id 拼接 后 作为统计维度，
统计以下数据:
总数量
Perfect数量:play_result=="Perfect" 的数量
Good数量:play_result=="Good" 的数量
Pass数量:play_result=="Pass" 的数量
Oops数量:play_result=="Oops" 的数量
Failed数量:play_result=="Failed" 的数量
Perfect+Good数量:play_result=="Perfect" 或 play_result=="Good" 的数量
Perfect比例:Perfect数量 / 总数量
Good比例:Good数量 / 总数量
Pass比例:Pass数量 / 总数量
Oops比例:Oops数量 / 总数量
Failed比例:Failed数量 / 总数量
Perfect+Good比例:Perfect+Good数量 / 总数量

导出为excel 命名: 步骤1文件 结尾追加 _stats.xlsx

需求三:
在需求二中， 追加从另外两个mysql表关联的组件配置字段:
MYSQL_HOST=xxx
MYSQL_USERNAME=xxx
MYSQL_PASSWORD=xxx
MYSQL_DATABASE=xxx
MYSQL_PORT=xxx

以上环境变量已配置在 .env 中。

1.如果 c_type 开头为"mid"

则读取下表:表名:middle_interaction_component

增加以下字段:
title
component_config
组件类型

其中:
 “组件类型”:  根据以下映射  把 c_type 转成中文名:xx互动
{
    "词汇类": {
        "物品互动": "mid_vocab_item",
        "图片互动": "mid_vocab_image",
        "填词互动": "mid_vocab_fillBlank",
        "指令互动": "mid_vocab_instruction"
    },
    "句子类": {
        "对话互动": "mid_sentence_dialogue",
        "语音互动": "mid_sentence_voice",
        "材料互动": "mid_sentence_material",
        "造句互动": "mid_sentence_makeSentence"
    },
    "语法类": {
        "挖空互动": "mid_grammar_cloze",
        "组句互动": "mid_grammar_sentence"
    },
    "发音类": {
        "发音互动": "mid_pron_pron"
    
}

2. 如果 c_type 开头为"core"
则读取下表:表名:core_interaction_component

增加以下字段:
title
component_config
组件类型

其中:
 “组件类型”:  根据以下映射  把 c_type 转成中文名:xx互动
{
    "口语类": {
        "口语快答": "core_speaking_reply",
        "口语妙问": "core_speaking_inquiry",
        "口语探讨": "core_speaking_explore"
        "口语独白": "core_speaking_monologue"
    },
    "阅读类": {
        "合作阅读": "core_reading_order",
    },
    "听力类": {
        "合作听力": "core_listening_order",
    },
    "写作类": {
        "看图组句": "core_writing_imgMakeSentence",
        "看图撰写": "core_writing_imgWrite",
        "问题组句": "core_writing_questionMakeSentence",
        "问题撰写": "core_writing_questionWrite",
    },
}

以上追加字段  增加到 步骤二输出的表中



"""

import os
from datetime import datetime
from dotenv import load_dotenv
import psycopg2
import pandas as pd
import pymysql

# ==================== 配置参数 ====================
# 时间范围配置（格式: "20250110"）
START_DATE = "20250915"  # 起始日期
END_DATE = "20251122"    # 截止日期

# 输出文件路径
OUTPUT_DIR = "output"

# 执行步骤控制
RUN_STEP1 = False  # 是否执行步骤1：数据导出
RUN_STEP2 = True  # 是否执行步骤2：数据统计
# ==================================================

# c_type 到中文组件类型的映射
C_TYPE_MAPPING = {
    # middle_interaction_component 映射
    "mid_vocab_item": "物品互动",
    "mid_vocab_image": "图片互动",
    "mid_vocab_fillBlank": "填词互动",
    "mid_vocab_instruction": "指令互动",
    "mid_sentence_dialogue": "对话互动",
    "mid_sentence_voice": "语音互动",
    "mid_sentence_material": "材料互动",
    "mid_sentence_makeSentence": "造句互动",
    "mid_grammar_cloze": "挖空互动",
    "mid_grammar_sentence": "组句互动",
    "mid_pron_pron": "发音互动",

    # core_interaction_component 映射
    "core_speaking_reply": "口语快答",
    "core_speaking_inquiry": "口语妙问",
    "core_speaking_explore": "口语探讨",
    "core_speaking_monologue": "口语独白",
    "core_reading_order": "合作阅读",
    "core_listening_order": "合作听力",
    "core_writing_imgMakeSentence": "看图组句",
    "core_writing_imgWrite": "看图撰写",
    "core_writing_questionMakeSentence": "问题组句",
    "core_writing_questionWrite": "问题撰写",
}


def step1_export_data():
    """步骤1：从数据库导出数据"""
    print("=" * 60)
    print("步骤1：数据导出")
    print("=" * 60)

    # 加载环境变量
    load_dotenv()

    # 获取数据库配置
    db_config = {
        'host': os.getenv('PG_DB_HOST'),
        'port': os.getenv('PG_DB_PORT'),
        'user': os.getenv('PG_DB_USER'),
        'password': os.getenv('PG_DB_PASSWORD'),
        'database': os.getenv('PG_DB_DATABASE')
    }

    # 转换时间格式
    start_datetime = datetime.strptime(START_DATE, "%Y%m%d").strftime("%Y-%m-%d 00:00:00")
    end_datetime = datetime.strptime(END_DATE, "%Y%m%d").strftime("%Y-%m-%d 23:59:59")

    print(f"时间范围: {start_datetime} ~ {end_datetime}")

    # 连接数据库
    conn = psycopg2.connect(**db_config)

    # 存储所有表的数据
    all_data = []

    # 遍历8个分表
    for i in range(8):
        table_name = f"user_component_play_record_{i}"
        print(f"正在读取表: {table_name}")

        # SQL查询
        query = f"""
        SELECT
            user_id,
            session_id,
            c_type,
            c_id,
            play_result,
            user_behavior_info,
            updated_at
        FROM {table_name}
        WHERE updated_at >= %s
          AND updated_at <= %s
          AND c_type IS NOT NULL
          AND c_id IS NOT NULL
        """

        # 执行查询
        df = pd.read_sql_query(query, conn, params=(start_datetime, end_datetime))
        all_data.append(df)
        print(f"  - 读取到 {len(df)} 条记录")

    # 关闭数据库连接
    conn.close()

    # 合并所有数据
    result_df = pd.concat(all_data, ignore_index=True)
    print(f"\n总共获取 {len(result_df)} 条记录")

    # 移除 updated_at 字段的时区信息（Excel不支持带时区的datetime）
    if 'updated_at' in result_df.columns and not result_df.empty:
        result_df['updated_at'] = result_df['updated_at'].dt.tz_localize(None)

    # 确保输出目录存在
    os.makedirs(OUTPUT_DIR, exist_ok=True)

    # 生成输出文件名
    output_filename = f"component_record_{START_DATE}_{END_DATE}.xlsx"
    output_path = os.path.join(OUTPUT_DIR, output_filename)

    # 导出到Excel
    result_df.to_excel(output_path, index=False, engine='openpyxl')
    print(f"数据已导出到: {output_path}")
    print()

    return output_path


def get_component_info_from_mysql(stats_df):
    """从MySQL获取组件配置信息"""
    # 加载环境变量
    load_dotenv()

    # 获取MySQL配置
    mysql_config = {
        'host': os.getenv('MYSQL_HOST'),
        'user': os.getenv('MYSQL_USERNAME'),
        'password': os.getenv('MYSQL_PASSWORD'),
        'database': os.getenv('MYSQL_DATABASE'),
        'port': int(os.getenv('MYSQL_PORT', 3306)),
        'charset': 'utf8mb4'
    }

    print("正在连接MySQL数据库...")
    conn = pymysql.connect(**mysql_config)

    try:
        # 分别处理 mid 和 core 类型的组件
        mid_records = stats_df[stats_df['c_type'].str.startswith('mid', na=False)][['c_type', 'c_id']]
        core_records = stats_df[stats_df['c_type'].str.startswith('core', na=False)][['c_type', 'c_id']]

        # 存储组件信息的字典，key 为 "c_type-c_id"
        component_info = {}

        # 查询 middle_interaction_component 表
        if not mid_records.empty:
            print(f"正在查询 middle_interaction_component 表，共 {len(mid_records)} 个组件...")

            # 获取唯一的 c_type 和 c_id 组合
            mid_unique = mid_records.drop_duplicates()

            for _, row in mid_unique.iterrows():
                c_type = row['c_type']
                c_id = row['c_id']

                query = """
                SELECT title, component_config
                FROM middle_interaction_component
                WHERE c_type = %s AND c_id = %s
                """
                result = pd.read_sql_query(query, conn, params=(c_type, c_id))

                if not result.empty:
                    key = f"{c_type}-{c_id}"
                    component_info[key] = {
                        'title': result['title'].iloc[0],
                        'component_config': result['component_config'].iloc[0]
                    }

            print(f"  - 查询到 {len([k for k in component_info.keys() if k.startswith('mid')])} 个组件信息")

        # 查询 core_interaction_component 表
        if not core_records.empty:
            print(f"正在查询 core_interaction_component 表，共 {len(core_records)} 个组件...")

            # 获取唯一的 c_type 和 c_id 组合
            core_unique = core_records.drop_duplicates()

            for _, row in core_unique.iterrows():
                c_type = row['c_type']
                c_id = row['c_id']

                query = """
                SELECT title, component_config
                FROM core_interaction_component
                WHERE c_type = %s AND c_id = %s
                """
                result = pd.read_sql_query(query, conn, params=(c_type, c_id))

                if not result.empty:
                    key = f"{c_type}-{c_id}"
                    component_info[key] = {
                        'title': result['title'].iloc[0],
                        'component_config': result['component_config'].iloc[0]
                    }

            print(f"  - 查询到 {len([k for k in component_info.keys() if k.startswith('core')])} 个组件信息")

    finally:
        conn.close()

    return component_info


def step2_statistics(input_file):
    """步骤2：数据统计"""
    print("=" * 60)
    print("步骤2：数据统计")
    print("=" * 60)

    # 读取步骤1导出的Excel文件，c_id作为字符串读取以保留前导零
    print(f"正在读取文件: {input_file}")
    df = pd.read_excel(input_file, engine='openpyxl', dtype={'c_id': str})
    print(f"读取到 {len(df)} 条记录")

    # 筛选 c_type 和 c_id 非空的记录
    df_filtered = df[(df['c_type'].notna()) & (df['c_id'].notna())].copy()
    print(f"筛选后 {len(df_filtered)} 条有效记录")

    # 确保c_type和c_id都是字符串类型（保留c_id的前导零）
    df_filtered['c_type'] = df_filtered['c_type'].astype(str)
    df_filtered['c_id'] = df_filtered['c_id'].astype(str)

    # 创建组件ID（c_type-c_id）
    df_filtered['component_id'] = df_filtered['c_type'] + '-' + df_filtered['c_id']

    # 按组件ID分组统计
    stats_list = []

    for component_id, group in df_filtered.groupby('component_id'):
        # 获取原始的 c_type 和 c_id
        c_type = group['c_type'].iloc[0]
        c_id = group['c_id'].iloc[0]

        # 总数量
        total_count = len(group)

        # 各状态数量
        perfect_count = len(group[group['play_result'] == 'Perfect'])
        good_count = len(group[group['play_result'] == 'Good'])
        pass_count = len(group[group['play_result'] == 'Pass'])
        oops_count = len(group[group['play_result'] == 'Oops'])
        failed_count = len(group[group['play_result'] == 'Failed'])
        perfect_good_count = len(group[group['play_result'].isin(['Perfect', 'Good'])])

        # 计算比例（保留两位小数）
        perfect_ratio = round(perfect_count / total_count, 2) if total_count > 0 else 0
        good_ratio = round(good_count / total_count, 2) if total_count > 0 else 0
        pass_ratio = round(pass_count / total_count, 2) if total_count > 0 else 0
        oops_ratio = round(oops_count / total_count, 2) if total_count > 0 else 0
        failed_ratio = round(failed_count / total_count, 2) if total_count > 0 else 0
        perfect_good_ratio = round(perfect_good_count / total_count, 2) if total_count > 0 else 0

        stats_list.append({
            'component_id': component_id,
            'c_type': c_type,
            'c_id': c_id,
            '总数量': total_count,
            'Perfect数量': perfect_count,
            'Good数量': good_count,
            'Pass数量': pass_count,
            'Oops数量': oops_count,
            'Failed数量': failed_count,
            'Perfect+Good数量': perfect_good_count,
            'Perfect比例': perfect_ratio,
            'Good比例': good_ratio,
            'Pass比例': pass_ratio,
            'Oops比例': oops_ratio,
            'Failed比例': failed_ratio,
            'Perfect+Good比例': perfect_good_ratio
        })

    # 创建统计结果DataFrame
    stats_df = pd.DataFrame(stats_list)

    print(f"统计了 {len(stats_df)} 个不同的组件")

    # 从MySQL获取组件配置信息
    print("\n" + "=" * 60)
    print("正在从MySQL获取组件配置信息...")
    print("=" * 60)
    component_info = get_component_info_from_mysql(stats_df)

    # 添加新字段：title, component_config, 组件类型
    # 使用 component_id (c_type-c_id) 作为 key 来匹配
    stats_df['title'] = stats_df['component_id'].apply(lambda x: component_info.get(x, {}).get('title', ''))
    stats_df['component_config'] = stats_df['component_id'].apply(lambda x: component_info.get(x, {}).get('component_config', ''))
    stats_df['组件类型'] = stats_df['c_type'].apply(lambda x: C_TYPE_MAPPING.get(x, ''))

    # 重新排列列顺序：将新增字段放在 c_type, c_id 后面
    columns_order = [
        'component_id', 'c_type', 'c_id',
        'title', 'component_config', '组件类型',  # 新增字段
        '总数量',
        'Perfect数量', 'Good数量', 'Pass数量', 'Oops数量', 'Failed数量', 'Perfect+Good数量',
        'Perfect比例', 'Good比例', 'Pass比例', 'Oops比例', 'Failed比例', 'Perfect+Good比例'
    ]
    stats_df = stats_df[columns_order]

    # 生成输出文件名（在原文件名后追加_stats）
    output_filename = os.path.basename(input_file).replace('.xlsx', '_stats.xlsx')
    output_path = os.path.join(OUTPUT_DIR, output_filename)

    # 导出到Excel
    stats_df.to_excel(output_path, index=False, engine='openpyxl')
    print(f"\n统计结果已导出到: {output_path}")
    print()

    return output_path


def main():
    export_file = None

    # 执行步骤1：数据导出
    if RUN_STEP1:
        export_file = step1_export_data()

    # 执行步骤2：数据统计
    if RUN_STEP2:
        # 如果步骤1没有执行，需要手动指定文件路径
        if export_file is None:
            export_file = os.path.join(OUTPUT_DIR, f"component_record_{START_DATE}_{END_DATE}.xlsx")
            if not os.path.exists(export_file):
                print(f"错误：找不到文件 {export_file}")
                print("请先执行步骤1或确保文件存在")
                return

        step2_statistics(export_file)

    print("=" * 60)
    print("处理完成！")
    print("=" * 60)


if __name__ == "__main__":
    main()
