"""
测试 8 个百炼 AI 应用的连通性。

通过 DashScope Application API 对每个应用发送一轮符合 P5 spec
首条 Prompt 结构的示例数据，验证百炼应用能正常返回。

调用方式：POST https://dashscope.aliyuncs.com/api/v1/apps/{APP_ID}/completion
参考文档：docs/reference/bailian-dashscope-api.md

用法：
    cd C:\\NeoZQYY && python scripts/ops/test_bailian_apps.py
"""

import json
import os
import sys
import time
from http import HTTPStatus
from pathlib import Path

from dotenv import load_dotenv

# 加载根 .env
load_dotenv(Path(__file__).resolve().parents[2] / ".env")

BAILIAN_API_KEY = os.environ.get("BAILIAN_API_KEY", "")
if not BAILIAN_API_KEY:
    raise RuntimeError("BAILIAN_API_KEY 未设置，请检查 .env 文件")

# 8 个应用 ID 映射
APP_CONFIGS = [
    ("BAILIAN_APP_ID_1_CHAT",        "应用1-通用对话"),
    ("BAILIAN_APP_ID_2_FINANCE",     "应用2-财务洞察"),
    ("BAILIAN_APP_ID_3_CLUE",        "应用3-客户数据维客线索分析"),
    ("BAILIAN_APP_ID_4_ANALYSIS",    "应用4-关系分析/任务建议"),
    ("BAILIAN_APP_ID_5_TACTICS",     "应用5-话术参考"),
    ("BAILIAN_APP_ID_6_NOTE",        "应用6-备注分析"),
    ("BAILIAN_APP_ID_7_CUSTOMER",    "应用7-客户分析"),
    ("BAILIAN_APP_ID_8_CONSOLIDATE", "应用8-维客线索整理"),
]

# ── 首条 Prompt 示例数据（符合 P5 spec 定义的 JSON 结构） ──────────────────────

FIRST_PROMPTS: dict[str, str] = {
    # 应用 1：通用对话 — 前端发起，首条消息为页面上下文
    "BAILIAN_APP_ID_1_CHAT": json.dumps({
        "source_page": "board-finance",
        "page_context": "当前在财务看板页面，本月收入 12,580 元，环比上月增长 8.3%",
        "screen_content": "财务看板：本月收入 12,580 元 | 台费 6,200 | 陪打 2,100 | 超休 1,000 | 商品 1,800 | 充值 1,480"
    }, ensure_ascii=False),

    # 应用 2：财务洞察
    "BAILIAN_APP_ID_2_FINANCE": json.dumps({
        "site_id": 1,
        "time_dimension": "本月",
        "date_range": {"start": "2026-03-01 08:00:00", "end": "2026-04-01 08:00:00"},
        "current_period": {
            "income_structure": {"table_fee": 6200, "assistant_pd": 2100, "assistant_cx": 1000, "goods": 1800, "recharge": 1480},
            "prepaid_assets": {"total_balance": 28500, "recharge_amount": 1480, "consumption_deduction": 2100},
            "expense_summary": {"rent": 3000, "utilities": 800, "supplies": 500, "salary": 4200, "other": 300},
            "platform_settlement": {"groupbuy_income": 1200, "platform_fee": 120, "net_income": 1080}
        },
        "previous_period": {
            "time_dimension": "上月",
            "date_range": {"start": "2026-02-01 08:00:00", "end": "2026-03-01 08:00:00"},
            "income_structure": {"table_fee": 5800, "assistant_pd": 1950, "assistant_cx": 950, "goods": 1600, "recharge": 1300},
            "prepaid_assets": {"total_balance": 29120, "recharge_amount": 1300, "consumption_deduction": 1950},
            "expense_summary": {"rent": 3000, "utilities": 750, "supplies": 450, "salary": 4200, "other": 250},
            "platform_settlement": {"groupbuy_income": 1000, "platform_fee": 100, "net_income": 900}
        }
    }, ensure_ascii=False),

    # 应用 3：客户数据维客线索分析
    "BAILIAN_APP_ID_3_CLUE": json.dumps({
        "member_nickname": "张三",
        "main_data": {
            "consumption_records": "近3个月消费12次，总消费2,860元。台费占比55%，助教费占比30%（陪打20%+超休10%），商品占比15%。偏好周末晚间时段，平均客单价238元。最近一次消费：2026-02-20，消费280元（台费+1小时助教）。",
            "member_cards": "持有银卡会员，2025-06-15注册",
            "card_balance_total": 580,
            "stored_value_balance_total": 580,
            "expected_visit_date": "2026-03-08",
            "days_since_last_visit": 13
        },
        "reference": {"app6_clues": None, "app8_history": []}
    }, ensure_ascii=False),

    # 应用 4：关系分析 / 任务建议
    "BAILIAN_APP_ID_4_ANALYSIS": json.dumps({
        "assistant_info": "花名：小李，级别：中级助教，工龄：1年3个月，擅长中式台球教学",
        "service_history": "累计服务张三8次，最近一次2026-02-20，平均每次服务1.5小时，客户评价良好",
        "task_assignment_basis": "优先召回：距上次到店13天，超过客户平均到店间隔（10天）",
        "customer_data": {
            "system_data": "近3个月消费12次，总消费2,860元，客单价238元，储值余额580元",
            "notes": [
                {"recorded_by": "小李", "content": "张三喜欢打中式台球，经常带朋友来，上次提到想学斯诺克", "created_at": "2026-02-20 21:30:00"},
                {"recorded_by": "小王", "content": "张三对充值活动比较感兴趣，上次充了500", "created_at": "2026-02-10 20:00:00"}
            ]
        },
        "reference": {"app8_current": None, "app8_history": []}
    }, ensure_ascii=False),

    # 应用 5：话术参考
    "BAILIAN_APP_ID_5_TACTICS": json.dumps({
        "assistant_info": "花名：小李，级别：中级助教，工龄：1年3个月",
        "service_history": "累计服务张三8次，最近一次2026-02-20",
        "task_assignment_basis": "优先召回：距上次到店13天",
        "customer_data": {
            "system_data": "近3个月消费12次，总消费2,860元，储值余额580元",
            "notes": [{"recorded_by": "小李", "content": "张三喜欢打中式台球，经常带朋友来", "created_at": "2026-02-20 21:30:00"}]
        },
        "task_suggestion": "{\"relationship_summary\":\"主要服务助教，累计服务8次，关系紧密\",\"task_description\":\"客户张三距上次到店已13天，超过其平均到店间隔10天，存在轻度流失风险。\",\"actions\":[{\"suggestion\":\"本周内通过微信联系张三，以斯诺克体验课为切入点邀请回店\"}],\"summary\":\"以斯诺克体验为切入点，本周内微信召回\"}",
        "reference": {"app8_history": []}
    }, ensure_ascii=False),

    # 应用 6：备注分析
    "BAILIAN_APP_ID_6_NOTE": json.dumps({
        "current_note": {
            "content": "张三今天带了两个朋友来，说是公司同事，三个人打了两个小时中式，走的时候说下周还想来，问有没有团建套餐",
            "recorded_by": "小李",
            "created_at": "2026-03-05 21:30:00"
        },
        "reference": {
            "member_nickname": "张三",
            "consumption_data": "近3个月消费12次，总消费2,860元，客单价238元",
            "all_notes": [
                {"recorded_by": "小李", "content": "张三喜欢打中式台球，经常带朋友来，上次提到想学斯诺克", "created_at": "2026-02-20 21:30:00"},
                {"recorded_by": "小王", "content": "张三对充值活动比较感兴趣，上次充了500", "created_at": "2026-02-10 20:00:00"}
            ],
            "app3_clues": None,
            "app8_history": []
        }
    }, ensure_ascii=False),

    # 应用 7：客户分析
    "BAILIAN_APP_ID_7_CUSTOMER": json.dumps({
        "member_id": 10086,
        "member_nickname": "张三",
        "objective_data": "近3个月消费12次，总消费2,860元，客单价238元。台费占比55%，助教费30%（陪打+超休），商品15%。偏好周末晚间，储值余额580元，银卡会员。距上次到店13天，平均到店间隔10天。",
        "subjective_data": {
            "notes": [
                {"recorded_by": "小李", "content": "张三喜欢打中式台球，经常带朋友来，上次提到想学斯诺克", "created_at": "2026-02-20 21:30:00"},
                {"recorded_by": "小王", "content": "张三对充值活动比较感兴趣，上次充了500", "created_at": "2026-02-10 20:00:00"},
                {"recorded_by": "小李", "content": "张三今天带了两个朋友来，说是公司同事，问有没有团建套餐", "created_at": "2026-03-05 21:30:00"}
            ]
        },
        "reference": {"app8_current": None, "app8_history": []}
    }, ensure_ascii=False),

    # 应用 8：维客线索整理
    "BAILIAN_APP_ID_8_CONSOLIDATE": json.dumps({
        "app3_clues": {
            "generated_at": "2026-03-05 14:30:00",
            "clues": [
                {"detail": "客户近3个月消费12次，平均10天到店一次，当前已13天未到店，超过平均间隔30%，存在轻度流失风险。建议本周内联系召回。", "category": "消费习惯", "summary": "到店间隔异常，轻度流失风险", "emoji": "⏰"},
                {"detail": "客户台费占比55%，助教费占比30%（陪打+超休），偏好周末晚间时段（18:00-22:00），中式台球为主。客单价238元，属于中等消费水平。", "category": "玩法偏好", "summary": "周末晚间中式台球爱好者", "emoji": "🎱"}
            ]
        },
        "app6_clues": {
            "generated_at": "2026-03-05 14:25:00",
            "clues": [
                {"category": "社交关系", "emoji": "👥", "detail": "客户经常带朋友来店，最近一次带了两位公司同事，主动询问团建套餐，具有社交裂变潜力和团建需求。", "summary": "常带朋友，有团建需求"},
                {"category": "促销接受", "emoji": "💰", "detail": "客户对充值活动感兴趣，曾主动充值500元，储值余额580元。可在余额接近用完时推荐充值优惠。", "summary": "充值意愿强，关注优惠活动"}
            ]
        }
    }, ensure_ascii=False),
}


# ── 通过 HTTP 调用百炼应用 API ─────────────────────────────────────────────────

import urllib.request
import urllib.error

API_BASE = "https://dashscope.aliyuncs.com/api/v1/apps"


def call_app(env_key: str, label: str) -> dict:
    """通过 HTTP POST 调用单个百炼应用，返回结果摘要。"""
    app_id = os.environ.get(env_key, "")
    if not app_id:
        return {"app": label, "status": "SKIP", "reason": f"环境变量 {env_key} 未设置"}

    prompt = FIRST_PROMPTS.get(env_key, "你好")
    url = f"{API_BASE}/{app_id}/completion"

    body = json.dumps({
        "input": {"prompt": prompt},
        "parameters": {"has_thoughts": False}
    }).encode("utf-8")

    headers = {
        "Authorization": f"Bearer {BAILIAN_API_KEY}",
        "Content-Type": "application/json",
    }

    t0 = time.time()
    try:
        req = urllib.request.Request(url, data=body, headers=headers, method="POST")
        with urllib.request.urlopen(req, timeout=60) as resp:
            data = json.loads(resp.read().decode("utf-8"))
        elapsed = round(time.time() - t0, 2)

        output = data.get("output", {})
        usage = data.get("usage", {})
        text = output.get("text", "")
        finish = output.get("finish_reason", "?")
        models = usage.get("models", [])
        tokens_info = {}
        if models:
            m = models[0]
            tokens_info = {
                "model": m.get("model_id", "?"),
                "input": m.get("input_tokens", "?"),
                "output": m.get("output_tokens", "?"),
            }

        return {
            "app": label,
            "app_id": app_id[:8] + "...",
            "status": "OK",
            "elapsed_s": elapsed,
            "finish_reason": finish,
            "tokens": tokens_info,
            "response_preview": text[:300] + ("..." if len(text) > 300 else ""),
        }
    except urllib.error.HTTPError as e:
        elapsed = round(time.time() - t0, 2)
        err_body = ""
        try:
            err_body = e.read().decode("utf-8")[:300]
        except Exception:
            pass
        return {
            "app": label,
            "app_id": app_id[:8] + "...",
            "status": "ERROR",
            "elapsed_s": elapsed,
            "error": f"HTTP {e.code}: {err_body}",
        }
    except Exception as e:
        elapsed = round(time.time() - t0, 2)
        return {
            "app": label,
            "app_id": app_id[:8] + "...",
            "status": "ERROR",
            "elapsed_s": elapsed,
            "error": str(e)[:300],
        }


def main():
    print("=" * 70)
    print("百炼 AI 应用连通性测试（DashScope Application API）")
    print(f"API Key: {BAILIAN_API_KEY[:8]}...{BAILIAN_API_KEY[-4:]}")
    print(f"API Base: {API_BASE}")
    print("=" * 70)

    results = []
    for env_key, label in APP_CONFIGS:
        print(f"\n▶ 测试 {label} ...")
        result = call_app(env_key, label)
        results.append(result)

        icon = "✅" if result["status"] == "OK" else "⏭️" if result["status"] == "SKIP" else "❌"
        print(f"  {icon} {result['status']}", end="")
        if "elapsed_s" in result:
            print(f" ({result['elapsed_s']}s)", end="")
        if "tokens" in result:
            t = result["tokens"]
            print(f" | model={t.get('model','?')} in={t.get('input','?')} out={t.get('output','?')}", end="")
        if "finish_reason" in result:
            print(f" | finish={result['finish_reason']}", end="")
        if "error" in result:
            print(f"\n  ⚠️ {result['error'][:150]}", end="")
        if "reason" in result:
            print(f" | {result['reason']}", end="")
        print()

        if "response_preview" in result:
            preview = result["response_preview"][:200]
            print(f"  📝 {preview}")

    # 汇总
    print("\n" + "=" * 70)
    ok = sum(1 for r in results if r["status"] == "OK")
    err = sum(1 for r in results if r["status"] == "ERROR")
    skip = sum(1 for r in results if r["status"] == "SKIP")
    print(f"汇总: ✅ {ok} 成功 | ❌ {err} 失败 | ⏭️ {skip} 跳过 | 共 {len(results)} 个应用")
    print("=" * 70)

    if err > 0:
        sys.exit(1)


if __name__ == "__main__":
    main()
