import re
from typing import Optional

import allure
from playwright.sync_api import Page, expect

from zhyy.test_case.Resource.UI.base_page import BasePage


class ContractManagementPage(BasePage):
    """合同管理页面对象。

    注意：
    - 当前未提供页面侦察结果，以下 selector 采用“候选定位 + TODO”方式。
    - 落地执行前，建议通过侦察脚本确认真实 DOM 后替换 TODO selector。
    """

    def __init__(self, page: Page, screenshot_dir: str):
        super().__init__(page, screenshot_dir)

    def open(self, url: str) -> None:
        with allure.step("打开合同管理被测页面"):
            self.goto(url)
            self.attach_screenshot("01_open_contract_management_page.png")

    def login_if_needed(self, username: str, password: str) -> None:
        """如页面跳转到登录页，则执行登录。

        TODO:
        请根据真实登录页 DOM 替换用户名、密码、登录按钮 selector。
        当前保留常见中文系统候选定位，避免在无侦察结果时硬编码单一 selector。
        """
        with allure.step("如需要则登录系统"):
            username_candidates = [
                self.page.get_by_placeholder(re.compile("用户名|账号|请输入用户名|请输入账号")),
                self.page.locator("input[name='username']"),
                self.page.locator("input[type='text']").first,
                # TODO: 替换为真实用户名输入框 selector，例如：self.page.locator("#username")
            ]

            password_candidates = [
                self.page.get_by_placeholder(re.compile("密码|请输入密码")),
                self.page.locator("input[name='password']"),
                self.page.locator("input[type='password']"),
                # TODO: 替换为真实密码输入框 selector，例如：self.page.locator("#password")
            ]

            login_button_candidates = [
                self.page.get_by_role("button", name=re.compile("登录|登 录|Login", re.I)),
                self.page.locator("button[type='submit']"),
                self.page.locator("text=登录"),
                # TODO: 替换为真实登录按钮 selector
            ]

            login_form_visible = any(
                locator.first.is_visible()
                for locator in username_candidates + password_candidates
            )

            if not login_form_visible:
                return

            self.fill_first_visible(username_candidates, username, description="用户名输入框")
            self.fill_first_visible(password_candidates, password, description="密码输入框")
            self.attach_screenshot("02_before_login.png")

            self.click_first_visible(login_button_candidates, description="登录按钮")
            self.wait_network_idle()
            self.attach_screenshot("03_after_login.png")

    def ensure_contract_menu_selected(self) -> None:
        """进入/确认合同菜单。

        若 URL 已直接进入合同管理页面，本方法不会强制点击菜单。
        TODO:
        如系统必须通过左侧菜单进入，请用页面真实菜单 selector 替换候选定位。
        """
        with allure.step("找到并选择合同菜单"):
            menu_candidates = [
                self.page.get_by_role("menuitem", name=re.compile("合同管理|合同")),
                self.page.get_by_text(re.compile("^合同管理$|^合同$"), exact=False),
                self.page.locator("text=合同管理"),
                self.page.locator("text=合同"),
                # TODO: 替换为真实合同菜单 selector
            ]

            try:
                menu = self.first_visible_locator(
                    menu_candidates,
                    timeout=3000,
                    description="合同菜单",
                )
                menu.click()
                self.wait_network_idle()
            except AssertionError:
                # 直接 URL 进入时可能没有可见菜单或菜单已选中，不阻断用例。
                pass

            self.attach_screenshot("04_contract_menu_selected.png")

    def search_by_contract_no_fuzzy(self, partial_contract_no: str) -> None:
        """按合同编号进行模糊查询。"""
        with allure.step(f"输入合同编号部分字符并点击查询：{partial_contract_no}"):
            contract_no_input_candidates = [
                self.page.get_by_placeholder(re.compile("合同编号|请输入合同编号")),
                self.page.locator("input[placeholder*='合同编号']"),
                self.page.locator("label:has-text('合同编号')").locator("xpath=following::input[1]"),
                self.page.locator("text=合同编号").locator("xpath=following::input[1]"),
                # TODO: 替换为真实合同编号搜索框 selector
            ]

            query_button_candidates = [
                self.page.get_by_role("button", name=re.compile("^查询$|搜索")),
                self.page.locator("button:has-text('查询')"),
                self.page.locator("text=查询"),
                # TODO: 替换为真实查询按钮 selector
            ]

            self.fill_first_visible(
                contract_no_input_candidates,
                partial_contract_no,
                description="合同编号搜索框",
            )
            self.attach_screenshot("05_filled_contract_no.png")

            self.click_first_visible(
                query_button_candidates,
                description="查询按钮",
            )
            self.wait_network_idle()

            # 等待常见表格渲染完成
            self.wait_for_result_table()
            self.attach_screenshot("06_after_contract_no_search.png")

    def wait_for_result_table(self) -> None:
        """等待查询结果表格出现。

        TODO:
        根据真实表格框架替换为唯一稳定 selector。
        """
        table_candidates = [
            self.page.locator(".el-table__body-wrapper"),
            self.page.locator(".el-table__body"),
            self.page.locator(".ant-table-tbody"),
            self.page.locator("table tbody"),
            self.page.locator("[role='table']"),
            # TODO: 替换为真实结果表格 selector
        ]
        self.first_visible_locator(table_candidates, timeout=10000, description="合同结果表格")

    def result_rows_locator(self):
        """结果表格行候选。

        TODO:
        建议替换为合同列表真实数据行 selector。
        """
        candidates = [
            self.page.locator(".el-table__body tbody tr"),
            self.page.locator(".ant-table-tbody tr"),
            self.page.locator("table tbody tr"),
            self.page.locator("[role='row']"),
            # TODO: 替换为真实合同列表行 selector
        ]

        for locator in candidates:
            try:
                if locator.count() > 0:
                    return locator
            except Exception:
                continue

        raise AssertionError("未找到合同列表数据行，请根据真实页面补充 result_rows_locator selector。")

    def get_result_row_texts(self) -> list[str]:
        with allure.step("获取合同列表查询结果"):
            rows = self.result_rows_locator()
            texts = self.text_contents(rows)
            # 过滤空行、表头、加载占位行
            return [
                text
                for text in texts
                if text and "暂无数据" not in text and "No Data" not in text
            ]

    def assert_contract_no_fuzzy_match(self, partial_contract_no: str) -> None:
        """断言查询结果与合同编号模糊查询条件一致。

        当前无页面侦察结果，无法确认“合同编号”列的真实 selector。
        因此先断言每条结果行文本中包含输入的合同编号部分字符。
        如果后续确认合同编号列 selector，请改为仅校验合同编号列文本。
        """
        with allure.step("断言列表仅展示合同编号与输入内容模糊匹配的数据"):
            row_texts = self.get_result_row_texts()

            assert row_texts, "查询结果为空，无法验证合同编号模糊匹配。请确认测试数据或查询条件。"

            unmatched_rows = [
                row_text
                for row_text in row_texts
                if partial_contract_no not in row_text
            ]

            assert not unmatched_rows, (
                f"存在与合同编号查询条件不匹配的结果。\n"
                f"查询条件：{partial_contract_no}\n"
                f"不匹配行：{unmatched_rows}"
            )
