import yaml
from typing import Optional, Set
from playwright.async_api import Page, Frame
from fsm_crawl.utils.domain import fqdn_from_url
from fsm_crawl.utils.data_paths import get_consent_manager_path
import asyncio


class PlaywrightConsentManager:
    def __init__(self, yaml_path: str = None):
        if yaml_path is None:
            yaml_path = get_consent_manager_path()
        
        with open(yaml_path, "r", encoding="utf-8") as f:
            self.cmps = yaml.safe_load(f)

    async def try_accept(self, page: Page, timeout_ms: int = 3000) -> bool:
        fqdn = fqdn_from_url(page.url)
        # print(f"[ConsentManager] Trying to accept cookies for {fqdn}")

        # Run all CMP handlers concurrently
        tasks = [self._handle_cmp(page, cmp, timeout_ms) for cmp in self.cmps]
        results = await asyncio.gather(*tasks, return_exceptions=True)

        for i, result in enumerate(results):
            if isinstance(result, Exception):
                continue
            if result:
                cmp_id = self.cmps[i].get("id", f"CMP_{i}")
                # print(f"[ConsentManager] Accepted: {cmp_id} for {fqdn}")
                return True

        # No CMP matched, but mark domain as attempted
        # print(f"[ConsentManager] Unable to accept cookies for {fqdn}")
        return False

    async def _handle_cmp(self, page: Page, cmp: dict, timeout_ms: int) -> bool:
        """
        Handle one CMP definition: try all actions in parallel where possible.
        """
        context: Page | Frame = page

        for action in cmp.get("actions", []):
            action_type = action["type"]
            value = action["value"]

            if action_type == "iframe":
                frame = await self._find_frame(page, value, timeout_ms)
                if not frame:
                    continue
                context = frame

            elif action_type == "css-selector":
                if await self._click(context, value, timeout_ms):
                    return True

            elif action_type == "css-selector-list":
                # Try all selectors concurrently
                tasks = [self._click(context, sel, timeout_ms) for sel in value]
                results = await asyncio.gather(*tasks, return_exceptions=True)
                if any(res is True for res in results):
                    return True

        return False

    async def _find_frame(self, page: Page, selector: str, timeout_ms: int) -> Optional[Frame]:
        try:
            iframe = await page.wait_for_selector(selector, timeout=timeout_ms)
            return await iframe.content_frame()
        except Exception:
            return None

    async def _click(self, context: Page | Frame, selector: str, timeout_ms: int) -> bool:
        try:
            locator = context.locator(selector)
            if await locator.first.is_visible(timeout=timeout_ms):
                await locator.first.click()
                return True
        except Exception:
            return False
        return False
