#!/usr/bin/env python3
"""HEL-177: 转账往来页 360/820/1440 冒烟截图（mock 接口，不依赖登录库）。"""

from __future__ import annotations

import json
import threading
from functools import partial
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path

from playwright.sync_api import sync_playwright

ROOT = Path(__file__).resolve().parents[1]
WEB = ROOT / "web"
OUT = ROOT / "hel177-shots"
OUT.mkdir(parents=True, exist_ok=True)

VIEWPORTS = [(1440, 900, "1440"), (820, 900, "820"), (360, 800, "360")]

SUMMARY = {
    "status": "ok",
    "own_company": {"id": 3, "name": "河南金牛煤业有限公司"},
    "window": {
        "start": "2026-01-01",
        "end": "2026-08-20",
        "has_opening": False,
        "opening": None,
        "ending": None,
    },
    "confirmed": {
        "outflow_total": "123860000.00",
        "outflow_count": 48,
        "inflow_total": "184205000.00",
        "inflow_count": 77,
        "net_change": "-60345000.00",
        "net_direction": "payable",
    },
    "pending": {"count": 3, "amount_total": "3200000.00"},
    "counterparties": [
        {
            "company_id": 5,
            "company_name": "河南金牛置业有限公司",
            "confirmed_outflow": "52000000.00",
            "confirmed_inflow": "86000000.00",
            "net": "-34000000.00",
            "pending_count": 2,
            "last_effective_at": "2026-08-18",
        },
        {
            "company_id": 6,
            "company_name": "河南金牛贸易有限公司",
            "confirmed_outflow": "10000000.00",
            "confirmed_inflow": "12605000.00",
            "net": "-2605000.00",
            "pending_count": 1,
            "last_effective_at": "2026-08-10",
        },
        {
            "company_id": 7,
            "company_name": "河南金牛农业科技发展有限公司",
            "confirmed_outflow": "61860000.00",
            "confirmed_inflow": "85600000.00",
            "net": "-23740000.00",
            "pending_count": 0,
            "last_effective_at": "2026-07-30",
        },
    ],
}

# 修正 mock：按 HEL-169 净变动=转出-转入，正数应收
SUMMARY["confirmed"] = {
    "outflow_total": "184205000.00",
    "outflow_count": 80,
    "inflow_total": "123860000.00",
    "inflow_count": 48,
    "net_change": "60345000.00",
    "net_direction": "receivable",
}
SUMMARY["counterparties"] = [
    {
        "company_id": 5,
        "company_name": "河南金牛置业有限公司",
        "confirmed_outflow": "86000000.00",
        "confirmed_inflow": "52000000.00",
        "net": "34000000.00",
        "pending_count": 2,
        "last_effective_at": "2026-08-18",
    },
    {
        "company_id": 6,
        "company_name": "河南金牛贸易有限公司",
        "confirmed_outflow": "12605000.00",
        "confirmed_inflow": "10000000.00",
        "net": "2605000.00",
        "pending_count": 1,
        "last_effective_at": "2026-08-10",
    },
    {
        "company_id": 7,
        "company_name": "河南金牛农业科技发展有限公司",
        "confirmed_outflow": "85600000.00",
        "confirmed_inflow": "61860000.00",
        "net": "23740000.00",
        "pending_count": 0,
        "last_effective_at": "2026-07-30",
    },
]

EVENTS = {
    "status": "ok",
    "window": {"start": "2026-01-01", "end": "2026-08-20"},
    "events": [
        {
            "event_id": 101,
            "direction": "in",
            "state": "confirmed",
            "pairing": "paired",
            "locked": False,
            "amount": "12000000.00",
            "currency": "CNY",
            "effective_at": "2026-08-18",
            "summary": "周转资金调拨",
            "status": "matched",
        },
        {
            "event_id": 102,
            "direction": "out",
            "state": "pending",
            "pairing": "unilateral",
            "locked": False,
            "amount": "1500000.00",
            "currency": "CNY",
            "effective_at": "2026-08-12",
            "summary": "工程款结算（待确认）",
            "status": "unresolved",
        },
    ],
    "next_cursor": None,
    "has_more": False,
}

DETAIL = {
    "status": "ok",
    "event": {
        "event_id": 101,
        "classification": "intercompany",
        "pairing": "paired",
        "status": "matched",
        "amount": "12000000.00",
        "currency": "CNY",
        "effective_at": "2026-08-18",
        "mode": "auto",
        "reason": "双边匹配",
        "counterparty": {
            "company_id": 5,
            "company_name": "河南金牛置业有限公司",
            "account_number_masked": "****8821",
        },
        "observations": [
            {
                "source_row_id": 1,
                "role": "payee",
                "source_row": 148,
                "sheet_name": "流水",
                "transaction_at": "2026-08-18 10:22:00",
                "income": "12000000.00",
                "expense": None,
                "import_batch_id": 19,
                "original_filename": "工行3305_202608.xls",
                "own_account_masked": "****3305",
                "counterparty_account_masked": "****8821",
                "counterparty_name": "河南金牛置业有限公司",
                "summary": "周转资金调拨",
                "reference": "JS20260818-00317",
            }
        ],
    },
}


def main() -> None:
    handler = partial(SimpleHTTPRequestHandler, directory=str(WEB))
    httpd = ThreadingHTTPServer(("127.0.0.1", 0), handler)
    port = httpd.server_address[1]
    threading.Thread(target=httpd.serve_forever, daemon=True).start()
    base = f"http://127.0.0.1:{port}"

    mock_js = f"""
    window.__HEL177_MOCK__ = true;
    const SUMMARY = {json.dumps(SUMMARY, ensure_ascii=False)};
    const EVENTS = {json.dumps(EVENTS, ensure_ascii=False)};
    const DETAIL = {json.dumps(DETAIL, ensure_ascii=False)};
    const _fetch = window.fetch.bind(window);
    window.fetch = async (input, init) => {{
      const url = String(input);
      if (url.includes('/api/me') || url.includes('/api/session')) {{
        return new Response(JSON.stringify({{role:'company', company_id:3, username:'牛女士', company_name:'河南金牛煤业有限公司'}}), {{status:200, headers:{{'Content-Type':'application/json'}}}});
      }}
      if (url.includes('/api/company/workspace')) {{
        return new Response(JSON.stringify({{status:'ok', pending_unilateral:0, pending_total:0, unilateral_events:[]}}), {{status:200, headers:{{'Content-Type':'application/json'}}}});
      }}
      if (url.includes('/api/company/intercompany/summary')) {{
        return new Response(JSON.stringify(SUMMARY), {{status:200, headers:{{'Content-Type':'application/json'}}}});
      }}
      if (url.includes('/api/company/intercompany/events')) {{
        return new Response(JSON.stringify(EVENTS), {{status:200, headers:{{'Content-Type':'application/json'}}}});
      }}
      if (url.includes('/api/company/transfer-events/')) {{
        return new Response(JSON.stringify(DETAIL), {{status:200, headers:{{'Content-Type':'application/json'}}}});
      }}
      if (url.includes('/api/company/')) {{
        return new Response(JSON.stringify({{status:'ok'}}), {{status:200, headers:{{'Content-Type':'application/json'}}}});
      }}
      return _fetch(input, init);
    }};
    """

    console_errors: list[str] = []
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_page()
        page.on("pageerror", lambda exc: console_errors.append(f"pageerror:{exc}"))
        page.on("console", lambda msg: console_errors.append(f"console:{msg.type}:{msg.text}") if msg.type == "error" else None)

        for width, height, label in VIEWPORTS:
            page.set_viewport_size({"width": width, "height": height})
            page.add_init_script(mock_js)
            # bypass auth guard
            page.add_init_script(
                """
                const orig = window.fetch;
                // initAuthGuard 读 /api/me；已在 mock 中处理
                """
            )
            page.goto(f"{base}/company.html", wait_until="domcontentloaded")
            page.evaluate(
                """() => {
                  // 若鉴权把页面踢走，强制停留
                  if (!document.body || document.body.dataset.portal !== 'company') return;
                }"""
            )
            # 等 summary 渲染
            page.wait_for_timeout(600)
            # 切到转账往来
            page.evaluate("() => { if (typeof showView === 'function') showView('transfers'); }")
            page.wait_for_selector("#transfersData:not([hidden])", timeout=5000)
            page.wait_for_timeout(300)
            overflow = page.evaluate(
                "() => document.documentElement.scrollWidth > document.documentElement.clientWidth + 1"
            )
            assert not overflow, f"{label} overview overflow"
            # 文案：期间净变动，无期末余额误用
            net_title = page.inner_text("#tfStatNetTitle")
            assert "期间净变动" in net_title, net_title
            page.screenshot(path=str(OUT / f"overview-{label}.png"), full_page=True)

            # 下钻明细
            page.click("#transfersCpBody tr.clickable")
            page.wait_for_selector("#transfersDetailLayer:not([hidden])", timeout=5000)
            page.wait_for_timeout(300)
            overflow2 = page.evaluate(
                "() => document.documentElement.scrollWidth > document.documentElement.clientWidth + 1"
            )
            assert not overflow2, f"{label} detail overflow"
            page.screenshot(path=str(OUT / f"detail-{label}.png"), full_page=True)

            # 打开抽屉
            page.click("[data-transfer-evidence]")
            page.wait_for_selector("#transferEvidenceDrawer.is-open", timeout=5000)
            page.wait_for_timeout(200)
            page.screenshot(path=str(OUT / f"drawer-{label}.png"), full_page=True)
            page.click("[data-close-transfer-evidence]")

        browser.close()

    hard = [e for e in console_errors if "Failed to load resource" not in e and "favicon" not in e]
    print("shots:", list(OUT.glob("*.png")))
    print("console_errors:", hard)
    if hard:
        raise SystemExit(1)
    print("OK")


if __name__ == "__main__":
    main()
