#!/usr/bin/env python3
"""
mitmproxy 插件：自动保存 TNG Money Packet 含 receiverList 的 API 响应。

用法:
  mitmdump -s debug-server/tng_mmp_mitm_addon.py -p 8888
  或
  mitmweb -s debug-server/tng_mmp_mitm_addon.py -p 8888

手机 WiFi 代理 -> PC_IP:8888，安装 mitmproxy CA 后打开 TNG 红包 Leaderboard。
命中响应会打印到终端，并写入 reverse/dumps/mitm_mmp/
"""

from __future__ import annotations

import json
import os
import re
from datetime import datetime

from mitmproxy import ctx, http

OUTPUT_DIR = os.path.join(
    os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
    "reverse",
    "dumps",
    "mitm_mmp",
)

MMP_HINTS = (
    "receiverlist",
    "claimedamount",
    "mmpreceiver",
    "moneypacket",
    "merchantmoneypacket",
)

HOST_HINTS = (
    "ebuckler.com",
    "tngdigital.com",
    "alipaydev.com",
)


def _ensure_dir() -> None:
    os.makedirs(OUTPUT_DIR, exist_ok=True)


def _looks_like_mmp(body: str) -> bool:
    lower = body.lower()
    if any(h in lower for h in MMP_HINTS):
        return True
    return "mmp" in lower and "amount" in lower


def _extract_receiver_list(obj):
    """递归找 receiverList 并格式化为 [(nickname, amount), ...]"""
    rows = []

    def walk(node):
        if isinstance(node, dict):
            if "receiverList" in node and isinstance(node["receiverList"], list):
                for item in node["receiverList"]:
                    if not isinstance(item, dict):
                        continue
                    name = (
                        item.get("nickName")
                        or item.get("displayName")
                        or item.get("userName")
                        or item.get("receiverName")
                        or item.get("name")
                    )
                    amount = (
                        item.get("claimedAmount")
                        or item.get("receiveAmount")
                        or item.get("amount")
                    )
                    if name and amount is not None:
                        rows.append((str(name), str(amount)))
            for v in node.values():
                walk(v)
        elif isinstance(node, list):
            for v in node:
                walk(v)

    walk(obj)
    return rows


class TngMmpCapture:
    def __init__(self) -> None:
        _ensure_dir()
        self.count = 0
        ctx.log.info(f"TNG MMP capture -> {OUTPUT_DIR}")

    def response(self, flow: http.HTTPFlow) -> None:
        if flow.response is None or not flow.response.content:
            return
        host = (flow.request.host or "").lower()
        if not any(h in host for h in HOST_HINTS):
            return
        try:
            text = flow.response.get_text(strict=False)
        except Exception:
            return
        if not text or not _looks_like_mmp(text):
            return

        self.count += 1
        ts = datetime.now().strftime("%Y%m%d_%H%M%S")
        safe_host = re.sub(r"[^\w.-]", "_", host)[:40]
        path = os.path.join(OUTPUT_DIR, f"mmp_{ts}_{self.count}_{safe_host}.json")

        summary_lines = [
            f"[TNG-MMP #{self.count}] {flow.request.method} {flow.request.url}",
        ]
        try:
            data = json.loads(text)
            with open(path, "w", encoding="utf-8") as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            rows = _extract_receiver_list(data)
            if rows:
                summary_lines.append(f"  receiverList ({len(rows)} 条):")
                for name, amount in rows:
                    summary_lines.append(f"    {name} -> {amount}")
            else:
                summary_lines.append("  (JSON 已保存，未解析到 receiverList)")
        except json.JSONDecodeError:
            with open(path, "w", encoding="utf-8") as f:
                f.write(text)
            summary_lines.append("  (非 JSON，已保存原文)")

        summary_lines.append(f"  saved: {path}")
        ctx.log.info("\n".join(summary_lines))


addons = [TngMmpCapture()]
