"""管理端 Page Object — 运费核算 + 运单审核 + 垫资打款 完整流程。

基于 resources/selectors/正常流程-web端.md 中的元素定位。
使用直接 hash URL 导航（比菜单点击更可靠），按钮选择器带 fallback 兜底。

URL 路由:
  运费核算:     #/tms/Carrier/freight
  运单审核:     #/tms/Carrier/OrderDetailAudit
  垫资-财务打款: #/tms/Carrier/dzFinancialapproval
  垫资-货主打款: #/tms/Carrier/dzApplicationForPayment
"""

from playwright.sync_api import Page

from tests.web.admin.pages.base_page import BasePage


class FreightAccountingPage(BasePage):
    """管理端 - 运费核算 / 运单审核 / 垫资打款。"""

    def __init__(self, page: Page, admin_base_url: str = ""):
        super().__init__(page)
        self.admin_base_url = admin_base_url

    # ════════════════════════════════════════════════════════════
    # URL 导航
    # ════════════════════════════════════════════════════════════

    def _go(self, hash_route: str):
        """直接 hash URL 跳转，比菜单点击稳定。"""
        url = f"{self.admin_base_url}/#/{hash_route.lstrip('#').lstrip('/')}"
        self.page.goto(url, wait_until="domcontentloaded")
        self.page.wait_for_timeout(2000)

    # ════════════════════════════════════════════════════════════
    # 通用操作
    # ════════════════════════════════════════════════════════════

    def _fill_order_no(self, order_no: str):
        """搜索栏输入运输单号（多个同名 input 时取第一个）。"""
        input_el = self.page.locator('input[placeholder="请输入运输单号"]').first
        self.wait_visible(input_el)
        input_el.clear()
        input_el.fill(order_no)

    def _click_search(self):
        """点击查询按钮。"""
        btn = self.page.locator("button:has-text('查询')").first
        self.wait_visible(btn)
        btn.click()
        self.page.wait_for_timeout(1000)

    def _table_has_data(self) -> bool:
        """检查查询结果是否有数据。"""
        try:
            self.page.wait_for_selector("table tbody tr", timeout=3_000)
            return True
        except Exception:
            return False

    def _click_table_action(self, button_index: int):
        """点击表格固定列操作按钮（纯图标 <i> 按钮，selector 文档路径 + fallback）。

        vxe-table 操作列按钮结构: td > div > button:nth-child(N) > span > i
        """
        # 精确路径（selector 文档原始定位）
        sel = (
            ".vxe-table--fixed-right-wrapper.scrolling--middle "
            ".vxe-table--body-wrapper.fixed-right--wrapper "
            "table > tbody > tr:nth-child(1) > td > div "
            f"> button:nth-child({button_index}) > span > i"
        )
        # 宽松路径（当 DOM 结构略有变化时兜底）
        sel_fallback = (
            ".vxe-table--fixed-right-wrapper "
            "table > tbody > tr:nth-child(1) "
            f"> td:last-child > div > button:nth-child({button_index})"
        )

        for attempt in range(3):
            try:
                self.page.wait_for_selector("table tbody tr", timeout=5_000)
                self.page.wait_for_timeout(300)
                btn = self.page.locator(sel).first
                if btn.count() == 0:
                    btn = self.page.locator(sel_fallback).first
                self.wait_visible(btn, timeout_ms=5_000)
                btn.click()
                self.page.wait_for_timeout(800)
                return
            except Exception:
                if attempt == 2:
                    raise
                self.page.wait_for_timeout(1_000)

    def _modal(self):
        """返回当前可见弹窗内容容器（.first 避免 strict mode）。"""
        return self.page.locator(
            ".vxe-modal--wrapper.is--visible .vxe-modal--body .vxe-modal--content"
        ).first

    def _scroll_modal(self):
        """滚动弹窗到底部。"""
        modal = self._modal()
        self.wait_visible(modal)
        modal.evaluate("el => el.scrollTop = el.scrollHeight")
        self.page.wait_for_timeout(500)

    def _retry_click_modal_btn(self, locator, timeout_ms: int = 5_000,
                               retries: int = 3, wait_after: int = 1500):
        """弹窗内按钮带重试点击（每次重试重新获取元素，防 DOM 置换）。"""
        for attempt in range(retries):
            el = locator() if callable(locator) else locator
            try:
                self.wait_visible(el, timeout_ms=timeout_ms)
                el.click()
                self.page.wait_for_timeout(wait_after)
                return
            except Exception:
                if attempt == retries - 1:
                    raise
                self.page.wait_for_timeout(1_000)

    # ════════════════════════════════════════════════════════════
    # 运费核算
    # ════════════════════════════════════════════════════════════

    def do_freight_accounting(self, order_no: str, retry_sql=None):
        """运费核算：导航→搜索→（无数据时 SQL 重试）→编辑→核算。

        Args:
            order_no: 运输单号
            retry_sql: 可调用对象，首次查询无数据时调用
        """
        self._go("tms/Carrier/freight")
        self._fill_order_no(order_no)
        self._click_search()

        # 无数据 → 执行 SQL 重试
        if not self._table_has_data():
            if retry_sql:
                retry_sql()
            self._fill_order_no(order_no)
            self._click_search()

        if not self._table_has_data():
            raise RuntimeError(f"运费核算查询无数据！单号: {order_no}")

        # 点击编辑 (button_index=4, 来自 selector 文档 Step 3)
        self._click_table_action(button_index=4)

        # 核算按钮 (selector 文档 Step 4)
        btn = self._modal().locator(
            "form > div:nth-child(4) button:nth-child(4) span"
        ).first
        self._retry_click_modal_btn(btn, wait_after=1500)
        self.screenshot("freight_accounting_done")

    # ════════════════════════════════════════════════════════════
    # 运单审核
    # ════════════════════════════════════════════════════════════

    def do_waybill_audit(self, order_no: str):
        """运单审核：导航→搜索→审核→滚动弹窗→审核通过。"""
        self._go("tms/Carrier/OrderDetailAudit")
        self._fill_order_no(order_no)
        self._click_search()

        # 点击审核 (button_index=2, selector 文档 Step 10)
        self._click_table_action(button_index=2)

        # 滚动弹窗 (selector 文档 Step 11)
        self._scroll_modal()

        # 审核通过 (selector 文档 Step 12)
        btn = self._modal().locator(
            "._btns > button.el-button.el-button--primary.el-button--medium > span"
        ).first
        self._retry_click_modal_btn(btn, wait_after=1500)
        self.screenshot("waybill_audit_done")

    # ════════════════════════════════════════════════════════════
    # 垫资-财务打款
    # ════════════════════════════════════════════════════════════

    def do_finance_payment(self, order_no: str, verify_code: str = "1234"):
        """垫资-财务打款：导航→搜索→在线支付→滚动→勾选光大/手机→验证码→确认。"""
        self._go("tms/Carrier/dzFinancialapproval")
        self._fill_order_no(order_no)
        self._click_search()

        # 在线支付 (button_index=2, selector 文档 Step 18)
        self._click_table_action(button_index=2)
        self._scroll_modal()

        # 勾选光大银行 (selector 文档 Step 20)
        try:
            radio = self._modal().locator(
                "form > div:nth-child(9) > div > div:nth-child(2) "
                "> label > span.el-radio__input > span"
            ).first
            self._retry_click_modal_btn(radio, timeout_ms=3_000, wait_after=300)
        except Exception:
            pass

        # 勾选手机号 (selector 文档 Step 21)
        try:
            radio = self.page.locator(
                "[id^='el-collapse-content-'] > div > div:nth-child(3) "
                "> label > span.el-radio__input > span"
            ).first
            self._retry_click_modal_btn(radio, timeout_ms=3_000, wait_after=300)
        except Exception:
            pass

        # 获取验证码 (selector 文档 Step 22)
        try:
            btn = self._modal().locator(
                "form > div.el-row > div.el-col.el-col-4 button span"
            ).first
            self._retry_click_modal_btn(btn, timeout_ms=3_000, wait_after=500)
        except Exception:
            pass

        # 输入验证码 (selector 文档 Step 23)
        try:
            inp = self._modal().locator(
                "form > div.el-row > div.el-col.el-col-20 input"
            ).first
            self.wait_visible(inp)
            inp.clear()
            inp.fill(verify_code)
        except Exception:
            pass

        # 确认打款 (selector 文档 Step 24)
        btn = self._modal().locator(
            "form > div:nth-child(12) > div > button span"
        ).first
        self._retry_click_modal_btn(btn, wait_after=1500)
        self.screenshot("finance_payment_done")

    # ════════════════════════════════════════════════════════════
    # 垫资-货主打款
    # ════════════════════════════════════════════════════════════

    def do_shipper_payment(self, order_no: str):
        """垫资-货主打款：导航→搜索→打款→勾选光大→确认打款→确认弹窗。"""
        self._go("tms/Carrier/dzApplicationForPayment")
        self._fill_order_no(order_no)
        self._click_search()

        # 打款 (button_index=2, selector 文档 Step 30)
        self._click_table_action(button_index=2)

        # 勾选光大 (selector 文档 Step 31)
        try:
            radio = self._modal().locator(
                "form > div > div:nth-child(3) > div > div > div:nth-child(3) "
                "> label > span.el-radio__label"
            ).first
            self._retry_click_modal_btn(radio, timeout_ms=3_000, wait_after=300)
        except Exception:
            pass

        # 确认打款 (selector 文档 Step 31-2)
        btn = self._modal().locator("> div > div > button > span").first
        self._retry_click_modal_btn(btn, wait_after=1000)

        # message-box 确认 (selector 文档 Step 32)
        try:
            msg = self.page.locator(
                "body > div.el-message-box__wrapper .el-message-box__btns "
                "> button.el-button.el-button--default.el-button--small.el-button--primary > span"
            )
            self._retry_click_modal_btn(msg, timeout_ms=5_000, wait_after=1500)
        except Exception:
            pass
        self.screenshot("shipper_payment_done")
