from __future__ import annotations

import argparse
import os
import sys
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Any

from dotenv import load_dotenv
from playwright.sync_api import Browser, BrowserContext, Page, Playwright, sync_playwright

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from e2e.pages.wanzhanggui_order_page import ManualOrderData, WanyoujiFulfillmentPage, WanzhangguiOrderPage
from e2e.tests.订单状态流转.上游发单.test_s_flow_006_dispatch_after_cancel_aftersale import (
    find_accounts_with_dispatch_strategy_name,
    latest_strategy_name_for_type,
)
from e2e.tests.订单状态流转.上游发单.test_s_flow_007_create_order_with_dispatch import (
    mark_s_flow_007_dispatched,
    manual_order_receive_price,
)
from e2e.tests.订单状态流转.上游发单.test_s_flow_014_accept_upstream_order_from_hall import (
    mark_s_flow_014_accepted,
)
from e2e.utils.account_store import E2EAccount, account_store


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


@dataclass
class Session:
    role: str
    browser: Browser
    context: BrowserContext
    page: Page


class SimpleBugReporter:
    def __init__(self, out_dir: Path, role: str):
        self.out_dir = out_dir
        self.role = role

    def clear(self) -> None:
        return

    def record_if_errors(self, **metadata: Any) -> None:
        try:
            text = str(metadata)
            (self.out_dir / f"{self.role}_reporter_events.txt").write_text(text, encoding="utf-8")
        except Exception:
            return


def launch_browser(
    playwright: Playwright,
    *,
    channel: str,
    headed: bool,
    slow_mo: int,
) -> Browser:
    return playwright.chromium.launch(
        channel=channel,
        headless=not headed,
        slow_mo=slow_mo,
        args=["--window-size=1440,900"],
    )


def new_session(
    browser: Browser,
    *,
    role: str,
    channel: str,
) -> Session:
    context = browser.new_context(viewport={"width": 1440, "height": 900}, screen={"width": 1440, "height": 900})
    page = context.new_page()
    setattr(page, "_e2e_dual_browser_role", role)
    setattr(page, "_e2e_dual_browser_channel", channel)
    return Session(role=role, browser=browser, context=context, page=page)


def pick_accounts(strategy_type: str) -> tuple[str, str, str, E2EAccount]:
    wanzhanggui_phone = os.getenv("E2E_DUAL_BROWSER_WANZHANGGUI_PHONE", "").strip() or os.getenv(
        "E2E_ORDER_WANZHANGGUI_PHONE",
        os.getenv("E2E_WANZHANGGUI_TENANT_PHONE", "19147509308"),
    )
    wanzhanggui_password = os.getenv("E2E_DUAL_BROWSER_WANZHANGGUI_PASSWORD", "").strip() or os.getenv(
        "E2E_ORDER_WANZHANGGUI_PASSWORD",
        os.getenv("E2E_WANZHANGGUI_TENANT_PASSWORD", "test123456"),
    )
    strategy_name = latest_strategy_name_for_type(wanzhanggui_phone, strategy_type)
    candidates = find_accounts_with_dispatch_strategy_name(
        wanzhanggui_phone,
        strategy_type,
        strategy_name,
        count=1,
    )
    explicit_wanyouji_phone = os.getenv("E2E_DUAL_BROWSER_WANYOUJI_PHONE", "").strip() or os.getenv(
        "E2E_ORDER_WANYOUJI_PHONE",
        "",
    ).strip()
    if explicit_wanyouji_phone:
        account = account_store().find(phone=explicit_wanyouji_phone)
        assert account is not None, f"指定丸友集账号不在台账中：{explicit_wanyouji_phone}"
        return wanzhanggui_phone, wanzhanggui_password, strategy_name, account
    assert candidates, f"台账中未找到已配置策略 {strategy_type}/{strategy_name} 的丸友集账号"
    return wanzhanggui_phone, wanzhanggui_password, strategy_name, candidates[0]


def build_order(strategy_name: str, strategy_type: str) -> ManualOrderData:
    timestamp = datetime.now().strftime("%m%d%H%M%S")
    return ManualOrderData(
        source_order_no=f"E2EORDER{timestamp}",
        order_title=f"E2E双浏览器已推单{timestamp}",
        strategy_name=strategy_name,
        strategy_type=strategy_type,
        receive_price=manual_order_receive_price(),
        dispatch_price=os.getenv("E2E_S_FLOW_DISPATCH_PRICE", "70"),
        role_name=f"Dual角色{timestamp[-4:]}",
    )


def dump_page(page: Page, out_dir: Path, name: str) -> None:
    target = out_dir / name
    target.mkdir(parents=True, exist_ok=True)
    (target / "body.txt").write_text(page.locator("body").inner_text(timeout=5000), encoding="utf-8")
    (target / "frames.txt").write_text("\n".join(frame.url for frame in page.frames), encoding="utf-8")
    page.screenshot(path=target / "screen.png", full_page=True)


def refresh_all_order_frames(page: Page) -> None:
    page.reload(wait_until="domcontentloaded")
    page.wait_for_timeout(1500)
    for frame in page.frames:
        if frame.url != page.url and "about:blank" not in frame.url:
            try:
                frame.evaluate("() => window.location.reload()")
            except Exception:
                continue
    page.wait_for_timeout(2500)


def run_chain(*, headed: bool, hold_seconds: int, slow_mo: int) -> Path:
    load_dotenv()
    strategy_type = os.getenv("E2E_ORDER_STRATEGY_TYPE", "单平台指定")
    timestamp = datetime.now().strftime("%Y%m%d-%H%M%S")
    out_dir = OUT_ROOT / timestamp
    out_dir.mkdir(parents=True, exist_ok=True)

    wanzhanggui_phone, wanzhanggui_password, strategy_name, wanyouji_account = pick_accounts(strategy_type)
    order = build_order(strategy_name, strategy_type)
    store = account_store()

    sessions: list[Session] = []
    browser: Browser | None = None
    with sync_playwright() as playwright:
        try:
            browser = launch_browser(
                playwright,
                channel="chrome",
                headed=headed,
                slow_mo=slow_mo,
            )
            wanyouji_session = new_session(browser, role="wanyouji", channel="chrome")
            wanzhanggui_session = new_session(browser, role="wanzhanggui", channel="chrome")
            sessions.extend([wanyouji_session, wanzhanggui_session])

            wanyouji = WanyoujiFulfillmentPage(
                wanyouji_session.page,
                TENANT_HOME_URL,
                SimpleBugReporter(out_dir, "wanyouji"),
            )
            wanzhanggui = WanzhangguiOrderPage(
                wanzhanggui_session.page,
                TENANT_HOME_URL,
                SimpleBugReporter(out_dir, "wanzhanggui"),
            )

            print(f"[dual-chain] Chrome login Wanyouji {wanyouji_account.phone}")
            wanyouji.login_main_account(wanyouji_account.phone, wanyouji_account.password)
            dump_page(wanyouji_session.page, out_dir, "01_chrome_wanyouji_logged_in")

            print(f"[dual-chain] Chrome login Wanzhanggui {wanzhanggui_phone}")
            wanzhanggui.login_main_account(wanzhanggui_phone, wanzhanggui_password)
            dump_page(wanzhanggui_session.page, out_dir, "02_chrome_wanzhanggui_logged_in")

            print(f"[dual-chain] S-FLOW-007 create dispatched order {order.source_order_no}")
            wanzhanggui.create_manual_order(order)
            main_order_no = wanzhanggui.find_main_order_no_from_order_list(
                source_order_no=order.source_order_no,
                order_title=order.order_title,
            )
            detail_text = wanzhanggui.assert_order_detail_statuses(
                main_order_no=main_order_no,
                source_order_no=order.source_order_no,
                order_title=order.order_title,
                expected_main_status="待接单",
                expected_sub_status_parts=["待接单", "已推单"],
                expected_sub_order_list_status="已发布",
                expected_aftersale_absent=True,
            )
            mark_s_flow_007_dispatched(
                wanzhanggui_phone=wanzhanggui_phone,
                main_order_no=main_order_no,
                order=order,
                strategy_name=strategy_name,
                strategy_type=strategy_type,
                wanyouji_accounts=[wanyouji_account],
                detail_status_text=detail_text,
                wanyouji_hall_status_texts=[],
                wanyouji_visibility="hall_check_pending_dual_browser",
            )
            dump_page(wanzhanggui_session.page, out_dir, "03_chrome_s_flow_007_created")

            expected_scope = "私单" if strategy_type == "单平台指定" else "公池单/私单"
            print("[dual-chain] Refresh Chrome Wanyouji before checking hall state")
            refresh_all_order_frames(wanyouji_session.page)
            hall_text = wanyouji.verify_order_reached_hall(
                order.source_order_no,
                order.order_title,
                expected_scope,
                main_order_no=main_order_no,
                expected_status="待接单",
            )
            mark_s_flow_007_dispatched(
                wanzhanggui_phone=wanzhanggui_phone,
                main_order_no=main_order_no,
                order=order,
                strategy_name=strategy_name,
                strategy_type=strategy_type,
                wanyouji_accounts=[wanyouji_account],
                detail_status_text=detail_text,
                wanyouji_hall_status_texts=[hall_text[:1500]],
                wanyouji_visibility="hall_visible_dual_browser",
            )
            dump_page(wanyouji_session.page, out_dir, "04_chrome_s_flow_007_hall_visible")
            if os.getenv("E2E_PROBE_STOP_AFTER_007", "").strip().lower() in {"1", "true", "yes"}:
                summary = "\n".join(
                    [
                        "S-FLOW-007 Chrome isolated-window probe passed",
                        f"wanzhanggui_chrome={wanzhanggui_phone}",
                        f"wanyouji_chrome={wanyouji_account.phone}",
                        f"strategy_type={strategy_type}",
                        f"strategy_name={strategy_name}",
                        f"source_order_no={order.source_order_no}",
                        f"main_order_no={main_order_no}",
                    ]
                )
                (out_dir / "summary.txt").write_text(summary, encoding="utf-8")
                print(summary)
                if hold_seconds > 0:
                    print(f"[dual-chain] holding browsers for {hold_seconds}s")
                    wanyouji_session.page.wait_for_timeout(hold_seconds * 1000)
                return out_dir

            print("[dual-chain] S-FLOW-014 accept order in Chrome Wanyouji")
            accepted_scope = wanyouji.accept_external_order_from_hall(
                order.source_order_no,
                order.order_title,
                expected_scope,
            )
            order_list_text = wanyouji.search_workshop_order_list(
                wanyouji.open_workshop_order_list(),
                order.source_order_no,
                order.order_title,
            )
            assert order.source_order_no in order_list_text or order.order_title in order_list_text, (
                f"丸友集接单后订单列表未找到订单：{order.source_order_no}"
            )
            mark_s_flow_014_accepted(
                store=store,
                source_order_no=order.source_order_no,
                wanyouji_phone=wanyouji_account.phone,
                accepted_scope=accepted_scope,
                order_list_text=order_list_text,
            )
            dump_page(wanyouji_session.page, out_dir, "05_chrome_s_flow_014_accepted")

            print("[dual-chain] Refresh Chrome Wanzhanggui before verifying accepted status")
            refresh_all_order_frames(wanzhanggui_session.page)
            accepted_detail_text = wanzhanggui.assert_order_detail_statuses(
                main_order_no=main_order_no,
                source_order_no=order.source_order_no,
                order_title=order.order_title,
                expected_main_status=["待接单", "代练中", "服务中"],
                expected_sub_status=["待接单-已推单", "代练中", "已接单"],
                expected_sub_order_list_status=["已接单", "代练中", "已发布"],
                expected_aftersale_absent=True,
            )
            mark_s_flow_014_accepted(
                store=store,
                source_order_no=order.source_order_no,
                wanyouji_phone=wanyouji_account.phone,
                accepted_scope=accepted_scope,
                order_list_text=order_list_text,
                wanzhanggui_after_wanyouji_accept_detail_status_text=accepted_detail_text[:1500],
            )
            dump_page(wanzhanggui_session.page, out_dir, "06_chrome_s_flow_014_verified_after_refresh")

            summary = "\n".join(
                [
                    "S-FLOW-007 + S-FLOW-014 dual-browser probe passed",
                    f"wanzhanggui_chrome={wanzhanggui_phone}",
                    f"wanyouji_chrome={wanyouji_account.phone}",
                    f"strategy_type={strategy_type}",
                    f"strategy_name={strategy_name}",
                    f"source_order_no={order.source_order_no}",
                    f"main_order_no={main_order_no}",
                    f"accepted_scope={accepted_scope}",
                ]
            )
            (out_dir / "summary.txt").write_text(summary, encoding="utf-8")
            print(summary)
            if hold_seconds > 0:
                print(f"[dual-chain] holding browsers for {hold_seconds}s")
                wanyouji_session.page.wait_for_timeout(hold_seconds * 1000)
            return out_dir
        finally:
            for session in reversed(sessions):
                try:
                    session.context.close()
                except Exception:
                    pass
            if browser is not None:
                try:
                    browser.close()
                except Exception:
                    pass


def main() -> None:
    parser = argparse.ArgumentParser(description="Run S-FLOW-007 + S-FLOW-014 with isolated Chrome windows.")
    parser.add_argument("--headed", action="store_true", help="Run visible Chrome windows. Kept for compatibility; this is now the default.")
    parser.add_argument("--headless", action="store_true", help="Run without visible browser windows.")
    parser.add_argument("--hold-seconds", type=int, default=0, help="Keep browsers open after the run.")
    parser.add_argument("--slow-mo", type=int, default=200, help="Playwright slow_mo in milliseconds.")
    parser.add_argument("--stop-after-007", action="store_true", help="Stop after S-FLOW-007 hall visibility verification.")
    args = parser.parse_args()
    if args.stop_after_007:
        os.environ["E2E_PROBE_STOP_AFTER_007"] = "1"
    run_chain(headed=not args.headless, hold_seconds=args.hold_seconds, slow_mo=args.slow_mo)


if __name__ == "__main__":
    main()
