# scraper2_hj3415/app/nfs/adapters/out/playwright/capabilities/wait.py
from __future__ import annotations

import asyncio
import time
from collections.abc import Mapping
from typing import Any

from logging_hj3415 import logger
from typing_extensions import override

from ._base import _PlaywrightBase  # pyright: ignore[reportPrivateUsage]


class PlaywrightWait(_PlaywrightBase):
    """
    wait 관련은 _page만 필요하므로 _PlaywrightBase 상속 안 해도 되지만,
    여기서는 self._page가 있다고 가정(PlaywrightBrowser가 베이스 제공).
    """

    async def sleep_ms(self, ms: int) -> None:
        await asyncio.sleep(ms / 1000)

    @override
    async def wait_attached(self, selector: str, *, timeout_ms: int = 10_000) -> None:
        await self._page.locator(selector).first.wait_for(
            state="attached", timeout=timeout_ms
        )

    async def wait_visible(self, selector: str, *, timeout_ms: int = 10_000) -> None:
        await self._page.locator(selector).first.wait_for(
            state="visible", timeout=timeout_ms
        )

    async def wait_table_nth_ready(
        self,
        table_selector: str,
        *,
        index: int,
        min_rows: int = 1,
        timeout_ms: int = 20_000,
        poll_ms: int = 200,
        context: Mapping[str, Any] | None = None,  # pyright: ignore[reportExplicitAny]
    ) -> None:
        ctx = dict(context or {})
        # URL은 항상 포함
        ctx.setdefault("url", self._page.url)
        ctx.setdefault("table_selector", table_selector)
        ctx.setdefault("table_index", index)
        ctx.setdefault("min_rows", min_rows)

        logger.debug(f"wait for table nth_ready | {ctx}")

        table = self._page.locator(table_selector).nth(index)
        await table.wait_for(state="attached", timeout=timeout_ms)

        rows = table.locator("tbody tr")
        deadline = time.monotonic() + timeout_ms / 1000

        cnt = 0
        while time.monotonic() < deadline:
            try:
                cnt = await rows.count()
            except Exception:
                cnt = 0

            if cnt >= min_rows:
                return

            await asyncio.sleep(poll_ms / 1000)
        ctx["last_cnt"] = cnt

        logger.warning(f"table rows timeout: last_cnt={cnt}, need>={min_rows} | {ctx}")
        raise TimeoutError(
            f"nth table not ready: index={index}, rows<{min_rows} | url={ctx.get('url')}"
        )

    async def wait_table_text_changed(
        self,
        table_selector: str,
        *,
        index: int,
        prev_text: str | None,
        min_rows: int = 1,
        min_lines: int = 50,
        timeout_sec: float = 12.0,
        poll_sec: float = 0.2,
        context: Mapping[str, Any] | None = None,  # pyright: ignore[reportExplicitAny]
    ) -> str:
        # 0) row 기준 ready
        await self.wait_table_nth_ready(
            table_selector,
            index=index,
            min_rows=min_rows,
            timeout_ms=int(timeout_sec * 1000),
            poll_ms=int(poll_sec * 1000),
            context=context,
        )

        start = time.monotonic()
        last_text = ""

        while True:
            loc = self._page.locator(table_selector).nth(index)
            try:
                text = await loc.inner_text()
            except Exception:
                text = ""

            lines = [ln.strip() for ln in text.splitlines() if ln.strip()]
            is_valid = len(lines) >= min_lines

            if is_valid:
                last_text = text
                if prev_text is None or text != prev_text:
                    return text

            if time.monotonic() - start >= timeout_sec:
                return last_text

            await asyncio.sleep(poll_sec)
