"""应用 6：备注分析（骨架）。

助教提交备注后自动触发，通过 AI 分析备注内容，
提取维客线索并评分。

返回 score（1-10）+ clues 数组。
评分规则：6 分为标准分，重复/低价值/时效性低酌情扣分，高价值信息酌情加分。
线索 category 限定 6 个枚举值。
线索提供者标记为当前备注提供人（context.noted_by_name）。

app_id = "app6_note"
"""

from __future__ import annotations

import asyncio
import json
import logging
from datetime import datetime

from app.ai.dashscope_client import DashScopeClient
from app.ai.cache_service import AICacheService
from app.ai.conversation_service import ConversationService
from app.ai.data_fetchers import fetch_member_consumption_data, fetch_member_notes
from app.ai.schemas import CacheTypeEnum

logger = logging.getLogger(__name__)

APP_ID = "app6_note"

# system message content 上限
_MAX_SYSTEM_CONTENT_LEN = 8000


def _default_member_data() -> dict:
    """数据获取失败时的默认空值。"""
    return {
        "member_nickname": "",
        "consumption_records": [],
        "member_cards": [],
        "card_balance_total": 0,
        "stored_value_balance_total": 0,
        "expected_visit_date": None,
        "days_since_last_visit": None,
    }


async def build_prompt(
    context: dict,
    cache_svc: AICacheService | None = None,
) -> list[dict]:
    """构建 Prompt 消息列表。

    并发获取消费数据和备注，失败时降级为空值。

    Args:
        context: 包含 site_id, member_id, note_content, noted_by_name
        cache_svc: 缓存服务，用于获取 reference 历史数据

    Returns:
        消息列表
    """
    site_id = context["site_id"]
    member_id = context["member_id"]
    note_content = context.get("note_content", "")
    noted_by_name = context.get("noted_by_name", "")
    noted_by_created_at = context.get("noted_by_created_at", "")

    # 并发获取消费数据和备注
    results = await asyncio.gather(
        fetch_member_consumption_data(site_id, member_id),
        fetch_member_notes(site_id, member_id),
        return_exceptions=True,
    )

    fetch_errors: list[str] = []

    if isinstance(results[0], Exception):
        logger.warning("App6 消费数据获取失败: %s", results[0])
        member_data = _default_member_data()
        fetch_errors.append("消费数据获取失败")
    else:
        member_data = results[0]

    if isinstance(results[1], Exception):
        logger.warning("App6 备注获取失败: %s", results[1])
        all_notes: list = []
        fetch_errors.append("备注获取失败")
    else:
        all_notes = results[1]

    # 构建 reference：App3 线索 + 最近 2 套 App8 历史
    reference = _build_reference(site_id, member_id, cache_svc)

    # 将消费数据和备注注入 reference
    reference["member_nickname"] = member_data.get("member_nickname", "")
    reference["consumption_data"] = {
        "consumption_records": member_data.get("consumption_records", []) or "该客户暂无消费记录",
        "member_cards": member_data.get("member_cards", []),
        "card_balance_total": member_data.get("card_balance_total", 0),
        "stored_value_balance_total": member_data.get("stored_value_balance_total", 0),
        "expected_visit_date": member_data.get("expected_visit_date"),
        "days_since_last_visit": member_data.get("days_since_last_visit"),
    }
    reference["all_notes"] = all_notes if all_notes else []

    system_content: dict = {
        "task": "分析备注内容，提取维客线索并评分。",
        "app_id": APP_ID,
        "rules": {
            "category_enum": [
                "客户基础", "消费习惯", "玩法偏好",
                "促销偏好", "社交关系", "重要反馈",
            ],
            "providers": noted_by_name,
            "scoring": "6 分为标准分，重复/低价值/时效性低酌情扣分，高价值信息酌情加分",
            "score_range": "1-10",
        },
        "output_format": {
            "score": "1-10 整数",
            "clues": [
                {
                    "category": "枚举值（6 选 1）",
                    "summary": "一句话摘要",
                    "detail": "详细说明",
                    "emoji": "表情符号",
                }
            ],
        },
        "current_time": datetime.now().strftime("%Y-%m-%d %H:%M"),
        "current_note": {
            "content": note_content,
            "recorded_by": noted_by_name,
            "created_at": noted_by_created_at,
        },
        "reference": reference,
    }

    if fetch_errors:
        system_content["_data_warnings"] = fetch_errors

    # Token 预算控制
    content_str = json.dumps(system_content, ensure_ascii=False, default=str)
    if len(content_str) > _MAX_SYSTEM_CONTENT_LEN:
        records = system_content["reference"].get("consumption_data", {}).get("consumption_records")
        if isinstance(records, list) and len(records) > 5:
            system_content["reference"]["consumption_data"]["consumption_records"] = records[:5]
            system_content["reference"]["consumption_data"]["_truncated"] = f"消费记录已截断，原始共 {len(records)} 条"
        content_str = json.dumps(system_content, ensure_ascii=False, default=str)
    if len(content_str) > _MAX_SYSTEM_CONTENT_LEN:
        n = system_content["reference"].get("all_notes")
        if isinstance(n, list) and len(n) > 10:
            system_content["reference"]["all_notes"] = n[:10]
            system_content["reference"]["_truncated_notes"] = f"备注已截断，原始共 {len(n)} 条"
        content_str = json.dumps(system_content, ensure_ascii=False, default=str)

    user_content = (
        f"请分析以下备注内容，提取维客线索并评分。\n"
        f"备注提供人：{noted_by_name}\n"
        f"备注内容：{note_content}\n"
        "返回 score（1-10 整数）和 clues 数组。"
        "category 必须是：客户基础、消费习惯、玩法偏好、促销偏好、社交关系、重要反馈 之一。"
    )

    return [
        {"role": "system", "content": content_str},
        {"role": "user", "content": user_content},
    ]


def _build_reference(
    site_id: int,
    member_id: int,
    cache_svc: AICacheService | None,
) -> dict:
    """构建 Prompt reference 字段。

    包含：
    - App3 客户数据线索（最新一条，如有）
    - 最近 2 套 App8 维客线索整理历史（附 generated_at）

    缓存不存在时返回空对象 {}。
    """
    if cache_svc is None:
        return {}

    reference: dict = {}
    target_id = str(member_id)

    # App3 客户数据线索
    app3_latest = cache_svc.get_latest(
        CacheTypeEnum.APP3_CLUE.value, site_id, target_id,
    )
    if app3_latest:
        reference["app3_clues"] = {
            "result_json": app3_latest.get("result_json"),
            "generated_at": app3_latest.get("created_at"),
        }

    # 最近 2 套 App8 历史
    app8_history = cache_svc.get_history(
        CacheTypeEnum.APP8_CLUE_CONSOLIDATED.value, site_id, target_id, limit=2,
    )
    if app8_history:
        reference["app8_history"] = [
            {
                "result_json": h.get("result_json"),
                "generated_at": h.get("created_at"),
            }
            for h in app8_history
        ]

    return reference


async def run(
    context: dict,
    client: DashScopeClient,
    cache_svc: AICacheService,
    conv_svc: ConversationService,
) -> dict:
    """执行 App6 备注分析。

    Args:
        context: site_id, member_id, note_content, noted_by_name
        bailian: 百炼客户端
        cache_svc: 缓存服务
        conv_svc: 对话服务

    Returns:
        百炼返回的结构化 JSON（score + clues 数组）
    """
    site_id = context["site_id"]
    member_id = context["member_id"]
    user_id = context.get("user_id", "system")
    nickname = context.get("nickname", "")

    # 1. 构建 Prompt
    messages = await build_prompt(context, cache_svc)

    # 2. 创建对话记录
    conversation_id = conv_svc.create_conversation(
        user_id=user_id,
        nickname=nickname,
        app_id=APP_ID,
        site_id=site_id,
        source_context={"member_id": member_id},
    )

    # 写入 system + user 消息
    conv_svc.add_message(
        conversation_id=conversation_id,
        role="system",
        content=messages[0]["content"],
    )
    conv_svc.add_message(
        conversation_id=conversation_id,
        role="user",
        content=messages[1]["content"],
    )

    # 3. 调用百炼 API
    result, tokens_used = await bailian.chat_json(messages)

    # 4. 写入 assistant 消息
    conv_svc.add_message(
        conversation_id=conversation_id,
        role="assistant",
        content=json.dumps(result, ensure_ascii=False),
        tokens_used=tokens_used,
    )

    # 5. 写入缓存（score 存入 ai_cache.score）
    score = result.get("score")
    cache_svc.write_cache(
        cache_type=CacheTypeEnum.APP6_NOTE_ANALYSIS.value,
        site_id=site_id,
        target_id=str(member_id),
        result_json=result,
        triggered_by=f"user:{user_id}",
        score=score,
    )

    logger.info(
        "App6 备注分析完成: site_id=%s member_id=%s score=%s conversation_id=%s tokens=%d",
        site_id, member_id, score, conversation_id, tokens_used,
    )

    return result
