#!/usr/bin/env python3
"""
宝锐工作台数据刷新 — OFFSET 分页全量 MCP 拉取 (cron-safe)
通过 HTTP JSON-RPC 直连飞书 MCP 端点，使用 OFFSET 翻页（session_id 在 cron 下会无限循环）
"""
import json
import os
import re
import sys
import time
import urllib.request
from datetime import datetime

# ── 配置 ──
MCP_URL = "https://project.feishu.cn/mcp_server/v1"
MCP_TOKEN = "m-abfb29e8-3104-434f-9944-8d0bb592f8cd"
DATA_DIR = "/Users/liuxinyuan/Desktop/Hermes输出-工作类/数据"
SALES_PK = "6593cd71471290e3cc6be6e6"
AFTERSALES_PK = "658288abfb8bd616b17025f1"

TYPE_ACTIVITY = "65ae1e5d44338dbe7c39a29a"
TYPE_CUSTOMER = "65ae1e403c87b152f3365ca6"
TYPE_ORDER = "662612aea6bb7089fea044ef"
TYPE_COMPLAINT = "6669433056a98249604376de"

# ── MCP 调用核心 ──
def mcp_call(method, args, timeout=120, retries=3):
    payload = {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "tools/call",
        "params": {"name": method, "arguments": args}
    }
    raw = None
    for attempt in range(1, retries + 1):
        try:
            req = urllib.request.Request(
                MCP_URL,
                data=json.dumps(payload).encode('utf-8'),
                headers={
                    "X-Mcp-Token": MCP_TOKEN,
                    "Content-Type": "application/json"
                }
            )
            with urllib.request.urlopen(req, timeout=timeout) as r:
                raw = r.read().decode('utf-8')
            break
        except Exception as e:
            if attempt == retries:
                print(f"  ❌ HTTP error (重试{retries}次后): {e}")
                return None
            print(f"  ⚠️ 第{attempt}次请求失败: {e}, 2秒后重试...")
            time.sleep(2)

    if raw is None:
        return None

    if raw.startswith("data:"):
        raw = raw.split("\n")[0].replace("data: ", "", 1)

    try:
        d = json.loads(raw)
    except Exception as e:
        print(f"  ❌ JSON parse error: {e}, raw[:300]={raw[:300]}")
        return None

    if "error" in d:
        print(f"  ❌ MCP error: {json.dumps(d['error'], ensure_ascii=False)[:200]}")
        return None

    if "result" not in d or "content" not in d["result"]:
        print(f"  ❌ Unexpected response: {json.dumps(d, ensure_ascii=False)[:500]}")
        return None

    for c in d["result"]["content"]:
        t = c.get("text", "")
        if not t:
            continue
        if "log_id" in t:
            continue
        try:
            cleaned = re.sub(r'\nlog_id:.*$', '', t.strip())
            return json.loads(cleaned)
        except Exception:
            pass
    return None


def parse_items(result):
    """解析 MQL search_by_mql 返回的 moql_field_list → 扁平 dict 列表"""
    items = []
    if not result:
        return items
    for gid, gitems in result.get("data", {}).items():
        if not isinstance(gitems, list):
            continue
        for item in gitems:
            fields = {}
            for f in (item.get("moql_field_list") or []):
                k = f["key"]
                v = f.get("value")
                if isinstance(v, list) and len(v) > 0:
                    if isinstance(v[0], dict) and "label" in v[0]:
                        fields[k] = v[0]["label"]
                        continue
                    v = v[0]
                if v is None:
                    fields[k] = ""
                elif isinstance(v, dict):
                    if "string_value" in v:
                        fields[k] = v["string_value"]
                    elif "double_value" in v:
                        fields[k] = v["double_value"]
                    elif "long_value" in v:
                        fields[k] = v["long_value"]
                    elif "key_label_value" in v:
                        fields[k] = v["key_label_value"]
                    elif "user_value" in v:
                        fields[k] = v["user_value"]
                    elif "user_value_list" in v:
                        fields[k] = v["user_value_list"]
                    elif "key_label_value_list" in v:
                        fields[k] = v["key_label_value_list"]
                    else:
                        fields[k] = str(v)
                else:
                    fields[k] = str(v)
            items.append(fields)
    return items


def pull_with_offset(pk, mql, max_pages=300, label=""):
    """OFFSET 分页拉全量 — cron 安全（session_id 在 cron 下无限循环）"""
    all_items = []
    seen_ids = set()
    offset = 0
    empty_streak = 0
    total_from_header = None
    
    for page in range(1, max_pages + 1):
        paginated_mql = f"{mql} LIMIT 50 OFFSET {offset}"
        result = mcp_call("search_by_mql", {
            "project_key": pk,
            "mql": paginated_mql,
            "session_id": ""
        }, timeout=60)
        
        if not result:
            print(f"  ⚠️ 第{page}页(OFFSET={offset}) HTTP失败，中断")
            break
        
        items = parse_items(result)
        
        if total_from_header is None and result.get("list"):
            total_from_header = result["list"][0].get("count", 0)
        
        if len(items) == 0:
            empty_streak += 1
            if empty_streak >= 2:
                print(f"  ✅ 第{page}页: 0条 (连续2页空，停止), 累计: {len(all_items)}")
                break
        else:
            empty_streak = 0
            # 按 work_item_id 去重（OFFSET 边界偶有重叠）
            new_count = 0
            for item in items:
                wid = item.get("work_item_id", "")
                if wid and wid in seen_ids:
                    continue
                if wid:
                    seen_ids.add(wid)
                all_items.append(item)
                new_count += 1
        
        print(f"  第{page}页(OFFSET={offset}): {len(items)}条, 新增: {new_count if len(items)>0 else 0}, 累计: {len(all_items)}{' / ~'+str(total_from_header) if total_from_header else ''}")
        offset += 50
    
    if total_from_header and len(all_items) < total_from_header:
        print(f"  🔴 警告: 拉取 {len(all_items)} 条 < 首屏总数 {total_from_header}，疑似中途超时截断！")
    return all_items


def write_json(filepath, data):
    os.makedirs(os.path.dirname(filepath), exist_ok=True)
    with open(filepath, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    size = os.path.getsize(filepath)
    count = len(data.get('items', data.get('customers', [])))
    print(f"  ✅ 写入: {count} 条, {size/1024:.1f}KB → {os.path.basename(filepath)}")


def validate_json(filepath):
    """验证 JSON 文件格式"""
    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            json.load(f)
        print(f"  ✅ JSON 验证通过: {os.path.basename(filepath)}")
        return True
    except Exception as e:
        print(f"  ❌ JSON 验证失败: {os.path.basename(filepath)} - {e}")
        return False


# ═══════════════════════════════════════════
# Main Pipeline
# ═══════════════════════════════════════════
RESULTS = {}
now = time.strftime("%Y-%m-%d %H:%M:%S")

# ═══ Step 1: 客诉 ═══
print("=" * 60)
print("Step 1: MCP 客诉数据 → mcp_complaints.json (OFFSET 分页)")
print("=" * 60)
try:
    mql = (
        "SELECT name, start_time, owner, current_status_operator, "
        "work_item_status, field_9e2144, field_3ce9fe, work_item_id "
        "FROM `售后管理`.`6669433056a98249604376de` "
        "ORDER BY start_time DESC"
    )
    complaints = pull_with_offset(AFTERSALES_PK, mql, max_pages=300, label="客诉")

    # 格式化输出
    output_items = []
    for c in complaints:
        item = dict(c)
        # operator 必须是数组（前端 renderComplaints 用 .forEach/.join）
        ops = item.get("current_status_operator", "")
        if isinstance(ops, dict) and "label" in ops:
            item["operator"] = [{"name_cn": ops["label"]}]
        elif isinstance(ops, list):
            item["operator"] = [{"name_cn": o.get("label", o.get("name_cn", str(o)))} for o in ops]
        elif isinstance(ops, str) and ops:
            item["operator"] = [{"name_cn": ops}]
        else:
            item["operator"] = []

        # 计算滞留天数
        st = item.get("start_time", "")
        if st:
            try:
                dt = datetime.fromisoformat(st.replace("Z", "+00:00"))
                item["days"] = (datetime.now() - dt.replace(tzinfo=None)).days
            except:
                item["days"] = 0

        # status 展平
        ws = item.get("work_item_status", "")
        if isinstance(ws, dict):
            item["status"] = ws.get("label", "")
        output_items.append(item)

    fp = os.path.join(DATA_DIR, "mcp_complaints.json")
    write_json(fp, {"updated": now, "total": len(output_items), "items": output_items})
    validate_json(fp)
    RESULTS["Step 1 客诉"] = f"✅ {len(output_items)} 条"
except Exception as e:
    print(f"  ❌ Step 1 失败: {e}")
    import traceback; traceback.print_exc()
    RESULTS["Step 1 客诉"] = f"❌ {e}"

# ═══ Step 2: 客户 ═══
print("\n" + "=" * 60)
print("Step 2: MCP 客户数据 → mcp_customers.json (OFFSET 分页)")
print("=" * 60)
try:
    mql = (
        "SELECT name, field_c3224b, field_5ed7ab, field_c8e80d, "
        "field_17186c, field_6415cf, work_item_id "
        "FROM `销售管理`.`65ae1e403c87b152f3365ca6` "
        "ORDER BY name"
    )
    customers = pull_with_offset(SALES_PK, mql, max_pages=100, label="客户")

    fp = os.path.join(DATA_DIR, "mcp_customers.json")
    write_json(fp, {"updated_at": now, "customers": customers})
    validate_json(fp)
    RESULTS["Step 2 客户"] = f"✅ {len(customers)} 条"
except Exception as e:
    print(f"  ❌ Step 2 失败: {e}")
    import traceback; traceback.print_exc()
    RESULTS["Step 2 客户"] = f"❌ {e}"

# ═══ Step 3: 活动 ═══
print("\n" + "=" * 60)
print("Step 3: MCP 活动数据 → daily_activities_mcp.json (OFFSET 分页)")
print("=" * 60)
try:
    mql = (
        "SELECT name, start_time, field_b99055, field_5f20fc, owner, work_item_id "
        "FROM `销售管理`.`65ae1e5d44338dbe7c39a29a` "
        "WHERE start_time >= '2026-07-01' "
        "ORDER BY start_time DESC"
    )
    activities = pull_with_offset(SALES_PK, mql, max_pages=300, label="活动")

    fp = os.path.join(DATA_DIR, "daily_activities_mcp.json")
    write_json(fp, {"updated": now, "total": len(activities), "items": activities})
    validate_json(fp)
    RESULTS["Step 3 活动"] = f"✅ {len(activities)} 条"
except Exception as e:
    print(f"  ❌ Step 3 失败: {e}")
    import traceback; traceback.print_exc()
    RESULTS["Step 3 活动"] = f"❌ {e}"

# ═══ Step 4: 8月订单 — 由 pull_orders.py 独立管线负责 ═══
print("\n" + "=" * 60)
print("Step 4: MCP 订单 — 跳过（pull_orders.py 富格式管线负责）")
print("=" * 60)
print("  ⏭️ mcp_orders_input.json 由 pull_orders.py 生成（含物流单号/收货人/购货单位等富字段），")
print("     shipment_pipeline.py 依赖其为 list 格式；本脚本最小 {updated,total,items} 格式会破坏它。")
print("     订单数据由顺丰 watchdog (15:00/22:00 cron) 独立刷新。")
RESULTS["Step 4 订单"] = "⏭️ 跳过（pull_orders.py 独立管线，勿覆盖富格式）"

# ═══ 汇总 ═══
print("\n" + "=" * 60)
print("MCP 数据拉取汇总")
print("=" * 60)
for k, v in RESULTS.items():
    print(f"  {k}: {v}")
print("\n全部 MCP 数据拉取完成。")
