from __future__ import annotations

import json
import os
import sys
from pathlib import Path

from dotenv import load_dotenv
from playwright.sync_api import Frame, Page, sync_playwright

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from e2e.pages.wanzhanggui_order_page import WanzhangguiOrderPage
from e2e.utils.account_store import account_store


OUTPUT_PATH = Path("runtime/probes/wanzhanggui_order_status_filters.json")


def visible_options(page: Page, frame: Frame) -> list[str]:
    texts: list[str] = []
    locators = [
        page.locator(".ant-select-dropdown:visible .ant-select-item-option-content"),
        frame.locator(".ant-select-dropdown:visible .ant-select-item-option-content"),
        page.locator(".ant-select-dropdown .ant-select-item-option-content"),
        frame.locator(".ant-select-dropdown .ant-select-item-option-content"),
        page.locator(".rc-virtual-list:visible .ant-select-item-option-content"),
        frame.locator(".rc-virtual-list:visible .ant-select-item-option-content"),
        page.locator(".rc-virtual-list .ant-select-item-option-content"),
        frame.locator(".rc-virtual-list .ant-select-item-option-content"),
    ]
    for locator in locators:
        try:
            count = locator.count()
        except Exception:
            continue
        for index in range(count):
            try:
                text = locator.nth(index).inner_text(timeout=1000).strip()
            except Exception:
                continue
            if text and text not in texts:
                texts.append(text)
    return texts


def dropdown_texts(page: Page, frame: Frame) -> list[dict[str, object]]:
    script = """() => {
        const visible = el => {
            const r = el.getBoundingClientRect();
            const style = window.getComputedStyle(el);
            return r.width > 0 && r.height > 0 && style.visibility !== 'hidden' && style.display !== 'none';
        };
        return Array.from(document.querySelectorAll('.ant-select-dropdown, .rc-virtual-list, [role="listbox"]'))
          .map(el => {
              const r = el.getBoundingClientRect();
              return {
                  text: (el.innerText || el.textContent || '').trim(),
                  visible: visible(el),
                  rect: { x: r.x, y: r.y, width: r.width, height: r.height },
                  className: el.className || '',
                  role: el.getAttribute('role') || ''
              };
          })
          .filter(item => item.text);
    }"""
    values: list[dict[str, object]] = []
    for scope in (page, frame):
        try:
            items = scope.evaluate(script)
        except Exception:
            continue
        for item in items:
            if item not in values:
                values.append(item)
    return values


def collect_all_select_diagnostics(page: Page, frame: Frame) -> list[dict[str, object]]:
    diagnostics: list[dict[str, object]] = []
    count = frame.locator(".ant-select-selector").count()
    for index in range(count):
        close_dropdown(page)
        selector = frame.locator(".ant-select-selector").nth(index)
        try:
            label = selector.locator("xpath=ancestor::*[contains(@class,'ant-form-item')][1]").inner_text(timeout=1000)
        except Exception:
            try:
                label = selector.inner_text(timeout=1000)
            except Exception:
                label = ""
        try:
            selector.click(timeout=2000)
            page.wait_for_timeout(800)
            options = visible_options(page, frame)
            drops = dropdown_texts(page, frame)
        except Exception as exc:
            options = []
            drops = [{"error": str(exc)}]
        diagnostics.append({"index": index, "label": label.strip(), "options": options, "dropdowns": drops})
    close_dropdown(page)
    return diagnostics


def close_dropdown(page: Page) -> None:
    page.keyboard.press("Escape")
    page.wait_for_timeout(500)


def click_filter_select(frame: Frame, label_keywords: list[str], fallback_index: int | None = None) -> bool:
    result = frame.evaluate(
        """({ labels, fallbackIndex }) => {
            const visible = el => {
                const r = el.getBoundingClientRect();
                const style = window.getComputedStyle(el);
                return r.width > 0 && r.height > 0 && style.visibility !== 'hidden' && style.display !== 'none';
            };
            const textOf = el => (el.innerText || el.textContent || '').replace(/\\s+/g, '');
            const items = Array.from(document.querySelectorAll('.ant-form-item, form > div, .el-form-item, .search-form, .filter-form, .ant-row > div'))
              .map(el => ({ el, text: textOf(el), r: el.getBoundingClientRect() }))
              .filter(item => visible(item.el));
            for (const label of labels) {
                const normalized = label.replace(/\\s+/g, '');
                const matched = items
                  .filter(item => item.text.includes(normalized))
                  .sort((a, b) => (a.r.width * a.r.height) - (b.r.width * b.r.height));
                for (const item of matched) {
                    const selector = item.el.querySelector('.ant-select-selector, .el-select, input[readonly]');
                    if (selector && visible(selector)) {
                        selector.click();
                        return { ok: true, by: label, text: item.text.slice(0, 120) };
                    }
                }
            }
            const selectors = Array.from(document.querySelectorAll('.ant-select-selector, .el-select, input[readonly]'))
              .filter(visible);
            if (fallbackIndex !== null && selectors[fallbackIndex]) {
                selectors[fallbackIndex].click();
                return { ok: true, by: 'fallback', count: selectors.length };
            }
            return { ok: false, count: selectors.length, labels };
        }""",
        {"labels": label_keywords, "fallbackIndex": fallback_index},
    )
    return bool(result and result.get("ok"))


def collect_filter_options(page: Page, frame: Frame, labels: list[str], fallback_index: int | None = None) -> list[str]:
    close_dropdown(page)
    if not click_filter_select(frame, labels, fallback_index):
        return []
    page.wait_for_timeout(1000)
    options = visible_options(page, frame)
    close_dropdown(page)
    return options


def main() -> None:
    load_dotenv()
    phone = os.getenv("E2E_ORDER_WANZHANGGUI_PHONE") or os.getenv("E2E_WANZHANGGUI_TENANT_PHONE") or "19147509308"
    password = os.getenv("E2E_ORDER_WANZHANGGUI_PASSWORD") or os.getenv("E2E_WANZHANGGUI_TENANT_PASSWORD") or "test123456"
    tenant_home_url = os.getenv("TENANT_HOME_URL", "https://test-lianlianyun.lianlianwz.com/home")

    stored = account_store().find(phone=phone)
    if stored and stored.password:
        password = stored.password

    with sync_playwright() as playwright:
        browser = playwright.chromium.launch(headless=os.getenv("E2E_HEADLESS", "1") not in {"0", "false", "False"})
        context = browser.new_context(viewport={"width": 1920, "height": 1080})
        page = context.new_page()
        order_page = WanzhangguiOrderPage(page, tenant_home_url)
        order_page.login_main_account(phone, password)
        frame = order_page.open_order_management()

        order_status = collect_filter_options(page, frame, ["订单状态"], fallback_index=2)
        sub_order_status = collect_filter_options(page, frame, ["子订单状态", "子单状态"], fallback_index=3)
        select_diagnostics = collect_all_select_diagnostics(page, frame)

        body_text = frame.locator("body").inner_text(timeout=5000)
        result = {
            "account": phone,
            "page_url": frame.url,
            "order_status_options": order_status,
            "sub_order_status_options": sub_order_status,
            "select_diagnostics": select_diagnostics,
            "page_text_sample": body_text[:3000],
        }
        OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
        OUTPUT_PATH.write_text(json.dumps(result, ensure_ascii=False, indent=2), encoding="utf-8")
        print(json.dumps(result, ensure_ascii=True, indent=2))
        context.close()
        browser.close()


if __name__ == "__main__":
    main()
