from __future__ import annotations

from collections.abc import Iterable

from playwright.sync_api import Locator, Page, expect


class BasePage:
    def __init__(self, page: Page):
        self.page = page

    def goto(self, url: str) -> None:
        self.page.goto(url, wait_until="domcontentloaded")

    def click_first_visible(self, selectors: Iterable[str], timeout: int = 3000) -> Locator:
        locator = self.find_first_visible(selectors, timeout=timeout)
        locator.click()
        return locator

    def fill_first_visible(self, selectors: Iterable[str], value: str, timeout: int = 3000) -> Locator:
        locator = self.find_first_visible(selectors, timeout=timeout)
        locator.fill(value)
        return locator

    def fill_and_verify(self, locator: Locator, value: str, timeout: int = 5000) -> None:
        expect(locator).to_be_visible(timeout=timeout)
        last_value = ""
        for _ in range(4):
            locator.click(force=True)
            locator.press("Control+A")
            locator.fill(value)
            try:
                expect(locator).to_have_value(value, timeout=1200)
                return
            except Exception:
                try:
                    locator.click(force=True)
                    locator.press("Control+A")
                    locator.type(value, delay=30)
                    expect(locator).to_have_value(value, timeout=1200)
                    return
                except Exception:
                    try:
                        last_value = locator.input_value(timeout=1000)
                    except Exception:
                        last_value = "<unreadable>"
                    self.page.wait_for_timeout(500)
        raise AssertionError(f"输入框未成功写入期望值 {value}，最后读取值：{last_value}")

    def find_first_visible(self, selectors: Iterable[str], timeout: int = 3000) -> Locator:
        last_error: Exception | None = None
        for selector in selectors:
            locators = self.page.locator(selector)
            try:
                count = locators.count()
            except Exception as exc:
                last_error = exc
                continue

            for index in range(count):
                locator = locators.nth(index)
                try:
                    expect(locator).to_be_visible(timeout=timeout)
                    return locator
                except Exception as exc:  # Playwright 不同版本会抛出不同的断言异常。
                    last_error = exc
        raise AssertionError(f"未找到可见选择器，已尝试：{list(selectors)}") from last_error

    def text_visible(self, text: str, timeout: int = 3000) -> bool:
        try:
            expect(self.page.get_by_text(text).first).to_be_visible(timeout=timeout)
            return True
        except Exception:
            return False

    def maybe_click_text(self, texts: Iterable[str], timeout: int = 1500) -> bool:
        for text in texts:
            locator = self.page.get_by_text(text, exact=False).first
            try:
                expect(locator).to_be_visible(timeout=timeout)
                locator.click()
                return True
            except Exception:
                continue
        return False

    def click_exact_text(self, text: str, timeout: int = 3000) -> Locator:
        locators = self.page.get_by_text(text, exact=True)
        last_error: Exception | None = None
        for index in range(locators.count()):
            locator = locators.nth(index)
            try:
                expect(locator).to_be_visible(timeout=timeout)
                locator.click()
                return locator
            except Exception as exc:
                last_error = exc
        raise AssertionError(f"未找到可见的精确文本：{text}") from last_error
