"""穿山甲 GroMore 数据 API 客户端测试。

重点锚定**签名算法**:用官方文档 Python / Java 示例里给出的「参数 + secure_key → sign」两个
测试向量做断言,保证我们的 build_sign 与穿山甲服务端逐字节一致(签错=接口直接 101 验签失败)。
另测拉取的翻页、业务码非 100 抛错、响应解析。
"""
from __future__ import annotations

import pytest

from app.core.config import settings
from app.integrations import pangle_report
from app.integrations.pangle_report import PangleReportError, build_sign

# ---- 签名:官方文档测试向量(完整 key 与期望 sign 来自文档代码注释)----

def test_build_sign_matches_doc_python_vector():
    params = {
        "current_time": "2022-01-06 16:10:54",
        "sign_type": "MD5",
        "version": "2.0",
        "user_id": 459,
        "role_id": 459,
    }
    sign = build_sign(params, "7f2ddb34d999ea61c00246ea3971529c")
    assert sign == "57b9fed9b6c09cefdced7fa770fe341d"


def test_build_sign_matches_doc_java_vector():
    params = {
        "user_id": 459,
        "role_id": 459,
        "version": "2.0",
        "sign_type": "MD5",
        "current_time": "2022-01-06 16:05:49",
    }
    sign = build_sign(params, "1c589c70f7bb2746027ce90c33d2544c")
    assert sign == "4d47b3a7679448c3da5c1d05e5291533"


def test_build_sign_drops_sign_and_empty_fields():
    """sign 字段与空值字段不参与签名:带上它们应得到与不带时相同的 sign。"""
    base = {"version": "2.0", "user_id": 459, "role_id": 459, "sign_type": "MD5"}
    with_noise = {**base, "sign": "deadbeef", "code_ids": "", "os": None}
    assert build_sign(with_noise, "key123") == build_sign(base, "key123")


# ---- 拉取:伪造 httpx 客户端,验证翻页 / 解析 / 业务码 ----

class _FakeResp:
    def __init__(self, payload: dict):
        self._payload = payload

    def raise_for_status(self) -> None:
        pass

    def json(self) -> dict:
        return self._payload


class _FakeClient:
    """按 offset 返回分页响应;记录每次请求的 params 供断言。"""

    def __init__(self, pages: list[dict]):
        self._pages = pages
        self.calls: list[dict] = []

    def __enter__(self):
        return self

    def __exit__(self, *a):
        return False

    def get(self, url: str, params: dict):
        self.calls.append(params)
        # 第 N 次调用取第 N 页(has_next 驱动翻页);超出用最后一页兜底
        page = self._pages[min(len(self.calls) - 1, len(self._pages) - 1)]
        return _FakeResp(page)


@pytest.fixture
def _configured(monkeypatch):
    monkeypatch.setattr(settings, "PANGLE_REPORT_USER_ID", 459)
    monkeypatch.setattr(settings, "PANGLE_REPORT_ROLE_ID", 459)
    monkeypatch.setattr(settings, "PANGLE_REPORT_SECURITY_KEY", "key123")


def test_fetch_daily_report_paginates_and_parses(monkeypatch, _configured):
    page1 = {
        "code": "100", "message": "",
        "data": {
            "has_next": "1", "currency": "cny", "total": 3,
            "report_list": [
                {"start_date": "2026-06-27", "site_id": "5830519", "ad_unit_id": "104142227",
                 "revenue": "1.23", "api_revenue": "1.05", "ecpm": "0.80", "imp_cnt": "1537"},
            ],
        },
    }
    page2 = {
        "code": "100", "message": "",
        "data": {
            "has_next": "0", "currency": "cny", "total": 3,
            "report_list": [
                {"start_date": "2026-06-27", "site_id": "5832303", "ad_unit_id": "104137445",
                 "revenue": "0.00", "api_revenue": "", "ecpm": "0", "imp_cnt": "12"},
            ],
        },
    }
    fake = _FakeClient([page1, page2])
    monkeypatch.setattr(pangle_report.httpx, "Client", lambda *a, **k: fake)

    rows = pangle_report.fetch_daily_report(start_date="2026-06-27", end_date="2026-06-27")

    assert len(rows) == 2          # 两页都被取到(has_next 驱动翻页)
    assert rows[0]["ad_unit_id"] == "104142227"
    assert rows[1]["api_revenue"] == ""
    # 每次请求都带上签名与必填鉴权参数
    assert "sign" in fake.calls[0]
    assert fake.calls[0]["user_id"] == 459 and fake.calls[0]["version"] == "2.0"


def test_fetch_daily_report_raises_on_business_error(monkeypatch, _configured):
    fail = {"code": "118", "message": "no revenue permission", "data": {}}
    monkeypatch.setattr(
        pangle_report.httpx, "Client", lambda *a, **k: _FakeClient([fail])
    )
    with pytest.raises(PangleReportError):
        pangle_report.fetch_daily_report(start_date="2026-06-27", end_date="2026-06-27")


def test_fetch_daily_report_no_config_raises(monkeypatch):
    monkeypatch.setattr(settings, "PANGLE_REPORT_USER_ID", 0)
    with pytest.raises(PangleReportError):
        pangle_report.fetch_daily_report(start_date="2026-06-27", end_date="2026-06-27")
