"""
数据分析MCP工具 - 提供多维分析计划投放数据的工具函数
"""
from typing import Dict, Any, List, Optional
from loguru import logger

from mcp_framework.mcp_tools import mcp
from mcp_framework.business.analysis import get_analysis_report as business_get_analysis_report
from mcp_framework.business.project import config as project_api
from mcp_framework.business.flow import flow as flow_api

@mcp.tool()
async def get_analysis_report(
    start_date: str,
    end_date: str,
    dimensions: List[str],
    campaign_ids: List[int] = [],
    plan_ids: List[int] = [],
    master_id: Optional[int] = None,
    adx_ids: List[int] = [],
    ep_ids: List[int] = [],
    slot_types: List[int] = [],
    flow_types: List[int] = [],
    # customs: List[str] = [],
    sort_field: Optional[str] = None,
    sort_type: Optional[str] = None,
    page: int = 1,
    page_size: int = 50,
    time_zone: int = None,
    optimize_target_id: Optional[int] = None,
    template_ids: List[int] = [],
    bid_type: Optional[int] = None,
    rta_strategy_ids: List[int] = [],
    report_mode: Optional[int] = None,
    supplier_chain: List[int] = [],
    deal_id: Optional[str] = None,
    slot_name: Optional[str] = None,
    slot_ids: List[int] = [],
    field_rules: List[Dict[str, Any]] = None,
    identify: Optional[str] = None
) -> Dict[str, Any]:
    """获取系统多维分析报表数据
    
    该接口用于灵活构建广告投放数据的多维分析报表，支持按照多种维度组合查询，
    并可以应用各种筛选条件和自定义指标。是数据分析和决策支持的核心工具。
    
    维度说明：
    dimensions参数决定了数据如何分组聚合，支持以下维度组合规则：
    1. reportDate（日期维度）可选，可以与任何其他维度组合
    2. reportHour（小时维度）可选，可以与任何其他维度组合
    3. 除了reportDate和reportHour外，其他维度只能选择一个，不能相互组合
    dimensions支持的维度包括：
    - reportDate: 日期维度（可选）
    - reportHour: 小时维度（可选）
    - planName: 计划名称，一般只作为展示使用
    - masterName: 广告主名称，一般只作为展示使用
    - campaignName: 活动名称，一般只作为展示使用
    - adxName: ADX名称
    - epName: EP名称
    - rtaName: 广告主RTA名称
    - pkg: 流量包名


    有效组合示例：
    - ["reportDate"] - 仅按日期分组
    - ["reportDate", "reportHour"] - 按日期和小时分组
    - ["reportDate", "planName"] - 按日期和计划名称分组
    - ["reportDate", "reportHour", "planName"] - 按日期、小时和计划名称分组
    
    注意：其他维度之间的组合可能导致API错误，请遵循上述规则构建维度组合
    
    Args:
        start_date: 必填，开始时间，格式为YYYY-MM-DD
        end_date: 必填，结束时间，格式为YYYY-MM-DD
        dimensions: 必填，报表维度，推荐使用文档中列出的维度组合
        campaign_ids: 活动ID列表，必须设置，若不需要则传空列表[]
        plan_ids: 计划ID列表，必须设置，若不需要则传空列表[]
        master_id: 广告主ID，单个广告主筛选
        adx_ids: ADX ID列表，必须设置，若不需要则传空列表[]
        ep_ids: EP ID列表，必须设置，若不需要则传空列表[]
        slot_types: 广告形式列表，必须设置，若不需要则传空列表[]
        flow_types: 流量类型列表，必须设置，若不需要则传空列表[]
        sort_field: 排序字段，指定结果排序的指标
        sort_type: 排序方式，DESC(降序)/ASC(升序)
        page: 页码，默认1
        page_size: 每页记录数，默认50
        time_zone: 时区，默认8(UTC+8)，非显示要求一般不指定，后端默认使用项目默认时区
        optimize_target_id: 优化目标ID，筛选特定优化目标的数据
        template_ids: 模版ID列表，必须设置，若不需要则传空列表[]
        bid_type: 出价类型，筛选特定出价方式(如CPM/CPC)
        rta_strategy_ids: RTA策略ID列表，必须设置，若不需要则传空列表[]
        report_mode: 设置类型: 0:全部；1:正常投放数据；3:ab测试数据
        supplier_chain: ssp层级列表，必须设置，若不需要则传空列表[]
        deal_id: 内部Deal id，筛选特定Deal
        slot_name: 广告位名称，模糊匹配广告位
        slot_ids: 广告位ID列表，必须设置，若不需要则传空列表[]
        field_rules: 具体过滤指标规则，用于按指标值过滤数据
        identify: 项目标识，用于区分不同的项目
        
    Returns:
      以下为简单的数据示例，其中真实的数据字段会非常多，同时提供了data_column_desc项目自定义指标描述
      {
          "code": 0,
          "msg": "success",
          "total": 10,
          "data": [
            {
              "reportDate": "2023-08-10",
              "planName": "测试RTA",
              "view": 12500,
              "click": 356,
              "clickRate": 2.85,
              "masterCost": 1258.65,
              "cpm": 100.69,
              "cpc": 3.54
            },
            {
              "reportDate": "2023-08-11",
              "planName": "测试RTA",
              "view": 15800,
              "click": 423,
              "clickRate": 2.68,
              "masterCost": 1456.32,
              "cpm": 92.17,
              "cpc": 3.44
            }
          ],
          "data_column_desc": {
            "reportDate": "日期",
            "planName": "计划名称",
            "view": "曝光量",
            "click": "点击量",
            "clickRate": "点击率(%)",
            "masterCost": "广告主花费",
            "cpm": "千次曝光成本",
            "cpc": "点击成本"
          }
        }
        
    """
    try:
        logger.info(f"MCP工具: 获取多维分析报表数据: 时间范围={start_date}~{end_date}")
        
        # 验证必需参数
        if not start_date or not end_date or not dimensions:
            return {
                "code": 400,
                "msg": "缺少必需参数: start_date, end_date 和 dimensions 为必填项",
                "data": [],
                "total": 0
            }
        
        # 这一版非常重要，需要根据上下文信息补充元数据完整
        if len(plan_ids) > 0:
          # 如果identify为None，则获取plan_ids[0]的project_id，然后获取project_id的identify
          result = await project_api.get_plan_project_map(plan_id = plan_ids[0])
          logger.debug(f"MCP工具: 获取项目元数据: 计划ID={plan_ids[0]}, 项目标识={result.get('data').get('identify')}")
          # if identify is None:
          identify = result.get("data").get("identify")
          campaign_id = result.get("data").get("campaignId")
          master_id = result.get("data").get("masterId")
          project_id = result.get("data").get("projectId")
          if time_zone is None:
            time_zone = result.get("data").get("timeZone")
          # else if len(campaign_ids) > 0:
          #   # 如果identify为None，则获取campaign_ids[0]的project_id，然后获取project_id的identify
          #   result = await project_api.get_campaign_project_map(campaign_id = campaign_ids[0])
          #   identify = result.get("identify")
        
        # 重新设计的维度校验逻辑
        # 2. 其他可用的维度列表
        valid_dimensions = [
            "reportDate", "reportHour", "planName", "masterName", "campaignName", 
            "adxName", "epName", "rtaName", "pkg"
        ]
        
        # 3. 校验所有维度都是有效的
        for dim in dimensions:
            if dim not in valid_dimensions:
                return {
                    "code": 400,
                    "msg": f"无效的维度: {dim}，请使用文档中列出的有效维度",
                    "data": [],
                    "total": 0
                }
        
        # 4. 除了reportDate和reportHour外，其他维度只能同时存在一个
        business_dimensions = [dim for dim in dimensions if dim not in ["reportDate", "reportHour"]]
        if len(business_dimensions) > 1:
            return {
                "code": 400,
                "msg": f"除了reportDate和reportHour外，其他维度只能选择一个，但您选择了多个: {business_dimensions}",
                "data": [],
                "total": 0
            }
        
        # 确保所有列表参数不为None
        campaign_ids = campaign_ids or []
        plan_ids = plan_ids or []
        adx_ids = adx_ids or []
        ep_ids = ep_ids or []
        slot_types = slot_types or []
        flow_types = flow_types or []
        template_ids = template_ids or []
        rta_strategy_ids = rta_strategy_ids or []
        supplier_chain = supplier_chain or []
        slot_ids = slot_ids or []

        # 排序信息设置，如果没设置就按照 view 曝光进行降序
        if sort_field is None:
            sort_field = "view"
        if sort_type is None:
            sort_type = "DESC"
        
        # 调用业务层API
        result = await business_get_analysis_report(
            report_type=15,  # 默认报表类型为15
            start_date=start_date,
            end_date=end_date,
            dimensions=dimensions,
            campaign_ids=campaign_ids,
            plan_ids=plan_ids,
            master_ids=None,  # 不使用master_ids参数
            master_id=master_id,
            adx_ids=adx_ids,
            ep_ids=ep_ids,
            slot_types=slot_types,
            flow_types=flow_types,
            customs= [],
            sort_field=sort_field,
            sort_type=sort_type,
            page=page,
            page_size=page_size,
            time_zone=time_zone,
            optimize_target_id=optimize_target_id,
            template_ids=template_ids,
            bid_type=bid_type,
            rta_strategy_ids=rta_strategy_ids,
            report_mode=report_mode,
            supplier_chain=supplier_chain,
            deal_id=deal_id,
            slot_name=slot_name,
            slot_ids=slot_ids,
            field_rules=field_rules,
            module="report_flow_list",
            identify=identify
        )

         # 如果数据获取成功，则调用 get_project_index 接口获取项目的指标，根据 dataColumn的colums的key过滤数据集的字段
        if result.get("code") == 0:
            project_index = await project_api.get_project_index(project_id=project_id, master_id=master_id, module="report_flow_list", identify=identify)
            if project_index.get("code") == 0:
                project_index_data = project_index.get("data")
                project_index_data_column = project_index_data.get("dataColumn")
                
                # 展平所有指标列
                all_index_columns = []
                for group in project_index_data_column:
                    all_index_columns.extend(group["columns"])
                
                # 创建指标字段描述映射
                result["data_column_desc"] = {col["key"]: col["title"] for col in all_index_columns}
                
                # 维度字段不应被过滤，需要保留
                dimension_fields = dimensions
                
                # 过滤数据时保留维度字段
                result["data"] = [
                    {k: v for k, v in item.items() if k in [col["key"] for col in all_index_columns] or k in dimension_fields}
                    for item in result["data"]
                ]
        return result
    except Exception as e:
        logger.error(f"获取多维分析报表数据失败: {str(e)}")
        return {
            "code": 500,
            "msg": f"获取数据失败: {str(e)}",
            "data": [],
            "total": 0
        }

@mcp.tool()
async def get_plan_diagnosis(plan_id: str, date: str, hour: int = None, time_zone : int = 8) -> Dict[str, Any]:
    """获取广告计划诊断数据
    
    Args:
        plan_id: 计划ID
        date: 查询日期，格式为YYYY-MM-DD
        hour: 0 - 23，需要查询的小时，注意：该参数不是必须的，如果不传则代表查询整天的数据，如果指定了小时，则查询指定小时数据
        time_zone: 项目的时区，默认8(UTC+8)，非显示要求一般不指定，后端默认使用项目默认时区

    Returns:
        计划诊断数据，包含以下信息：
        - 总请求量(req)
        - 竞价成功量(bid)
        - 各环节过滤流量数量(status1-status40)，如：
          * ADX定向不匹配(status1)
          * 操作系统不匹配(status2)
          * 地域不匹配(status3)
          * ...等多达40种过滤原因

        - 过滤原因的漏斗排序如下：
            id	状态	描述
            175	status1	ADX定向不匹配
            177	status2	操作系统不匹配
            179	status3	地域不匹配
            181	status4	包名定向不匹配
            183	status5	人群定向不匹配
            185	status6	设备品牌不匹配
            187	status7	广告形式不匹配
            189	status8	模板不匹配
            191	status9	EP定向不匹配
            253	status40	前置RTA缓存Reject
            193	status10	落地页协议(http/https)不符合要求
            195	status11	流量类型(APP/WAP/PC)不匹配
            197	status12	二级广告形式不匹配
            199	status13	流量Deeplink方式(直接/H5)与计划要求不匹配
            201	status14	计划当前小时不投放
            203	status15	文字长度不符合要求
            205	status16	图片(大小/格式)不符合要求
            207	status17	视频(时长/大小/格式)不符合要求
            209	status18	流量不支持广告主行业
            255	status39	流量未携带设备号
            211	status19	计划频次(曝光/点击)超限
            213	status20	活动频次(曝光/点击)超限
            215	status21	广告主频次(曝光/点击)超限
            217	status22	单次请求避免重复展现
            219	status23	被系统预算控制策略过滤
            221	status24	被系统创意优选策略淘汰
            223	status25	广告与流量相似度过低
            225	status26	被分媒体预算分配策略过滤
            227	status27	DPA商品推荐异常
            229	status28	计划出价小于广告位底价
            231	status29	过滤已转化用户
            233	status30	流量被反作弊策略过滤
            235	status31	设备价格/上市时间不匹配
            237	status32	SSP定向不匹配
            239	status33	请求禁止投放该广告主
            241	status34	计划投放APP不符合请求要求
            243	status35	计划优先级较低
            245	status36	内部竞价失败
            247	status37	被RTA策略过滤
            249	status38	多个同价广告，随机选择未命中
    """

    if time_zone is None:
        result = await project_api.get_plan_project_map(plan_id = plan_id)
        time_zone = result.get("data").get("timeZone")
    else:
        time_zone = 8
    
    result = await flow_api.get_plan_flow_info(plan_id, date, hour, time_zone )
    
    if result.get("success") and result.get("data"):
        # 获取诊断数据
        diagnosis_data = result["data"]
        
        # 添加状态码说明（可选）
        status_desc = {
            "status1": "ADX定向不匹配",
            "status2": "操作系统不匹配",
            "status3": "地域不匹配",
            "status4": "包名定向不匹配",
            "status5": "人群定向不匹配",
            "status6": "设备品牌不匹配",
            "status7": "广告形式不匹配",
            "status8": "模板不匹配",
            "status9": "EP定向不匹配",
            "status10": "落地页协议(http/https)不符合要求",
            "status11": "流量类型(APP/WAP/PC)不匹配",
            "status12": "二级广告形式不匹配",
            "status13": "流量Deeplink方式(直接/H5)与计划要求不匹配",
            "status14": "计划当前小时不投放",
            "status15": "文字长度不符合要求",
            "status16": "图片(大小/格式)不符合要求",
            "status17": "视频(时长/大小/格式)不符合要求",
            "status18": "流量不支持广告主行业",
            "status19": "计划频次(曝光/点击)超限",
            "status20": "活动频次(曝光/点击)超限",
            "status21": "广告主频次(曝光/点击)超限",
            "status22": "单次请求避免重复展现",
            "status23": "被系统预算控制策略过滤",
            "status24": "被系统创意优选策略淘汰",
            "status25": "广告与流量相似度过低",
            "status26": "被分媒体预算分配策略过滤",
            "status27": "DPA商品推荐异常",
            "status28": "计划出价小于广告位底价",
            "status29": "过滤已转化用户",
            "status30": "流量被反作弊策略过滤",
            "status31": "设备价格/上市时间不匹配",
            "status32": "SSP定向不匹配",
            "status33": "请求禁止投放该广告主",
            "status34": "计划投放APP不符合请求要求",
            "status35": "计划优先级较低",
            "status36": "内部竞价失败",
            "status37": "被RTA策略过滤",
            "status38": "多个同价广告，随机选择未命中",
            "status39": "流量未携带设备号",
            "status40": "前置RTA缓存Reject"
        }
        
        # 此处可以选择是否添加状态说明到返回数据
        result["status_descriptions"] = status_desc
    
    return result