from __future__ import annotations

import json
import os
import sys
from datetime import datetime
from pathlib import Path

from dotenv import load_dotenv
from playwright.sync_api import sync_playwright

ROOT_DIR = Path(__file__).resolve().parents[1]
if str(ROOT_DIR) not in sys.path:
    sys.path.insert(0, str(ROOT_DIR))
load_dotenv(ROOT_DIR / ".env")

from e2e.pages.wanyouji_member_page import WanyoujiMemberData, WanyoujiMemberPage
from e2e.pages.wanzhanggui_order_page import WanyoujiFulfillmentPage
from e2e.utils.account_store import account_store
from e2e.utils.image_captcha import ImageCaptchaSolver, decode_data_image
from e2e.utils.verification_code import FeishuConfig, VerificationCodeProvider


TENANT_HOME_URL = "https://test-lianlianyun.lianlianwz.com/home"
OUT_DIR = Path("runtime/probes/player_grab_captcha")


def main() -> None:
    payload = account_store().load_payload()
    order = sorted(
        [
            item
            for item in payload.get("dispatched_orders", [])
            if item.get("status") == "task_dispatched_to_player_hall"
        ],
        key=lambda item: item.get("updated_at", item.get("created_at", "")),
        reverse=True,
    )[0]
    player_phone = order.get("task_player_phone", "")
    player = account_store().find(phone=player_phone)
    if player is None:
        raise SystemExit(f"未找到打手账号：{player_phone}")

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False, slow_mo=120)
        context = browser.new_context(viewport={"width": 1920, "height": 1080})
        page = context.new_page()
        try:
            code_provider = VerificationCodeProvider(
                mode=os.getenv("E2E_VERIFICATION_CODE_MODE", "feishu"),
                static_code=os.getenv("E2E_VERIFICATION_CODE_STATIC", ""),
                manual_code_file=os.getenv("E2E_MANUAL_CODE_FILE", "runtime/sms_code.txt"),
                manual_timeout_seconds=int(os.getenv("E2E_MANUAL_CODE_TIMEOUT_SECONDS", "180")),
                manual_poll_interval_seconds=int(os.getenv("E2E_MANUAL_CODE_POLL_INTERVAL_SECONDS", "2")),
                feishu_config=FeishuConfig(
                    app_id=os.getenv("FEISHU_APP_ID", ""),
                    app_secret=os.getenv("FEISHU_APP_SECRET", ""),
                    chat_id=os.getenv("FEISHU_CHAT_ID", ""),
                    timeout_seconds=int(os.getenv("FEISHU_TIMEOUT_SECONDS", "60")),
                    poll_interval_seconds=int(os.getenv("FEISHU_POLL_INTERVAL_SECONDS", "3")),
                ),
            )
            member_page = WanyoujiMemberPage(page, TENANT_HOME_URL, code_provider=code_provider)
            member_page.verify_member_can_login_by_sms(
                WanyoujiMemberData(
                    phone=player.phone,
                    password=player.password,
                    role="打手",
                    name=player.contact_name,
                    product="丸友集",
                )
            )

            fulfillment = WanyoujiFulfillmentPage(page, TENANT_HOME_URL)
            frames = fulfillment.open_player_order_hall()
            target_frame = None
            for frame in frames:
                fulfillment.search_order_if_possible(frame, order["source_order_no"])
                text = frame.locator("body").inner_text(timeout=5000)
                if order["source_order_no"] in text or order["order_title"] in text:
                    target_frame = frame
                    break
            if target_frame is None:
                dump(page, "not_found", order)
                raise AssertionError(f"抢单大厅未找到订单：{order['source_order_no']}")

            clicked = fulfillment.click_row_action(
                target_frame,
                order["source_order_no"],
                order["order_title"],
                ["立即抢单", "抢单"],
            )
            if not clicked:
                dump(page, "no_grab_button", order)
                raise AssertionError("未点到立即抢单")
            page.wait_for_timeout(1500)
            probe_captcha_reads(page, target_frame, fulfillment, order)
            dump(page, "grab_modal", order)
            print(f"已保存打手抢单验证码弹框探测：{OUT_DIR.resolve()}")
        finally:
            page.wait_for_timeout(3000)
            context.close()
            browser.close()


def probe_captcha_reads(page, frame, fulfillment: WanyoujiFulfillmentPage, order: dict) -> None:
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
    solver = ImageCaptchaSolver(mode=os.getenv("E2E_IMAGE_CAPTCHA_MODE", "ddddocr"))
    rows = []
    for index in range(1, 11):
        src = fulfillment.grab_order_graphic_captcha_src(frame)
        image_bytes = decode_data_image(src)
        image_path = OUT_DIR / f"{timestamp}-captcha-{index:02d}-{order['source_order_no']}.png"
        image_path.write_bytes(image_bytes)
        try:
            raw = solver.solve(image_bytes)
        except Exception as exc:
            raw = f"<ocr failed: {exc}>"
        digits = "".join(ch for ch in raw if ch.isdigit())
        rows.append(
            {
                "index": index,
                "image": str(image_path),
                "raw": raw,
                "digits": digits,
                "digits_len": len(digits),
            }
        )
        if len(digits) == 4:
            break
        fulfillment.refresh_grab_order_graphic_captcha(frame)
        page.wait_for_timeout(500)
    report_path = OUT_DIR / f"{timestamp}-captcha-ocr-{order['source_order_no']}.json"
    report_path.write_text(json.dumps(rows, ensure_ascii=False, indent=2), encoding="utf-8")
    print(f"验证码 OCR 探测：{report_path.resolve()}")


def dump(page, name: str, order: dict) -> None:
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
    prefix = OUT_DIR / f"{timestamp}-{name}-{order['source_order_no']}"
    page.screenshot(path=prefix.with_suffix(".png"), full_page=True)
    data = {
        "url": page.url,
        "order": order,
        "frames": [],
    }
    for index, frame in enumerate(page.frames):
        frame_data = {
            "index": index,
            "url": frame.url,
            "text": "",
            "modal": {},
        }
        try:
            frame_data["text"] = frame.locator("body").inner_text(timeout=3000)
            frame_data["modal"] = frame.evaluate(
                """() => {
                    const visible = el => {
                      const r = el.getBoundingClientRect();
                      return r.width > 0 && r.height > 0;
                    };
                    const roots = Array.from(document.querySelectorAll('.ant-modal'))
                      .filter(visible);
                    const root = roots[roots.length - 1];
                    if (!root) return { found: false };
                    const describe = el => {
                      const r = el.getBoundingClientRect();
                      return {
                        tag: el.tagName,
                        className: String(el.className || ''),
                        text: (el.innerText || el.textContent || '').trim(),
                        value: el.value || '',
                        placeholder: el.getAttribute?.('placeholder') || '',
                        type: el.getAttribute?.('type') || '',
                        role: el.getAttribute?.('role') || '',
                        contenteditable: el.getAttribute?.('contenteditable') || '',
                        rect: { x: r.x, y: r.y, width: r.width, height: r.height },
                        html: (el.outerHTML || '').slice(0, 500),
                      };
                    };
                    return {
                      found: true,
                      text: (root.innerText || root.textContent || '').trim(),
                      inputs: Array.from(root.querySelectorAll('input, textarea, [contenteditable="true"], taro-input-core')).map(describe),
                      images: Array.from(root.querySelectorAll('img, canvas, svg')).map(describe),
                      buttons: Array.from(root.querySelectorAll('button, a, [role="button"]')).map(describe),
                      html: root.outerHTML.slice(0, 5000),
                    };
                }"""
            )
        except Exception as exc:
            frame_data["text"] = f"<read failed: {exc}>"
        data["frames"].append(frame_data)
    prefix.with_suffix(".json").write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")


if __name__ == "__main__":
    main()
