"""Scraper rejestru radców prawnych.

Kontekst: oficjalny rejestr KIRP to szukajradcy.pl (kirp.pl/krajowa-wyszukiwarka
osadza ten sam formularz w iframe). szukajradcy.pl/robots.txt blokuje ścieżkę
/search, więc bezpośredni scraping listingu przez `requests` jest niedozwolony.

Rozwiązanie: listing pobieramy przez Apify aktora `apify/web-scraper`, który
renderuje stronę w prawdziwej przeglądarce (Apify ma własne zasady compliance
i własną pulę proxy). Aktor przechodzi po stronach wyników i wyciąga linki
profili `/lawyers/...`, a następnie z każdego profilu wyciąga dane kontaktowe.

Tryby pracy (priorytet):
1. `seed_urls=[...]` — gotowa lista profili (np. ręczna lub z innego źródła).
2. Apify (`apify/web-scraper`) — domyślnie, jeśli jest APIFY_API_KEY.
3. Brak — loguje ostrzeżenie i kończy bez wyników.
"""

from __future__ import annotations

import logging
import re
from typing import Iterator

import requests
from bs4 import BeautifulSoup

from .. import config
from ..notifier import is_billing_error, notify_billing_error
from .base import Lead, ScraperSource

log = logging.getLogger(__name__)

BASE = "https://szukajradcy.pl"
SEARCH_URL = f"{BASE}/search"
DEFAULT_QUERY = "prawo rodzinne"

APIFY_BASE = "https://api.apify.com/v2"


# pageFunction wykonywany przez apify/web-scraper w przeglądarce.
# Na stronach listingu wyłuskuje linki profili i je enqueue'uje.
# Na stronach profilu wyciąga tekst do późniejszego parsowania w Pythonie.
_PAGE_FUNCTION = r"""
async function pageFunction(context) {
    const { request, $, log, enqueueRequest } = context;
    const url = request.url;

    if (url.includes('/lawyers/')) {
        // strona profilu — zwracamy surowy tekst, parsowanie po stronie Pythona
        const title = $('h1').first().text().trim();
        const text = $('body').text().replace(/\s+/g, ' ').trim();
        return {
            type: 'profile',
            url,
            title,
            text,
        };
    }

    // strona listingu — enqueue profili i kolejnych stron wyników
    const links = new Set();
    $('a[href*="/lawyers/"]').each((_, el) => {
        const href = $(el).attr('href');
        if (href) links.add(new URL(href, url).toString());
    });
    for (const profileUrl of links) {
        await enqueueRequest({ url: profileUrl, userData: { label: 'profile' } });
    }
    log.info(`[oirp] listing ${url} → ${links.size} profiles`);
    return null; // listing sam w sobie nie jest rekordem
}
"""


class OirpScraper(ScraperSource):
    name = "oirp"

    def __init__(
        self,
        limit: int | None = None,
        query: str | None = None,
        max_pages: int | None = None,
        seed_urls: list[str] | None = None,
        actor: str | None = None,
        api_key: str | None = None,
    ):
        super().__init__(limit=limit)
        self.query = query or config.APIFY_OIRP_QUERY
        self.max_pages = max_pages or config.APIFY_OIRP_MAX_PAGES
        self.seed_urls = seed_urls or []
        self.actor = actor or config.APIFY_OIRP_ACTOR
        self.api_key = api_key or config.APIFY_API_KEY
        self.max_items = config.APIFY_OIRP_MAX_ITEMS

    # ---------- parsowanie pojedynczego profilu ----------
    def _lead_from_text(self, url: str, title: str | None, text: str) -> Lead | None:
        person_name = (title or "").strip() or None

        email = self.extract_email(text)
        phone = self.extract_phone(text)

        street = postal = city = None
        m = re.search(r"Adres:\s*([^\n]+?)(?:\s{2,}|$)", text)
        if m:
            addr = m.group(1).strip()
            pm = re.search(r"(.+?),\s*(\d{2}-\d{3})\s*([^\n,]*)", addr)
            if pm:
                street = pm.group(1).strip()
                postal = pm.group(2).strip()
                city = (pm.group(3) or "").strip() or None

        kancelaria = None
        km = re.search(r"(Kancelaria[^\n]{2,120}?)(?:\s{2,}|Adres:|Telefon|E-?mail|$)", text)
        if km:
            kancelaria = km.group(1).strip().rstrip(":,.")

        if kancelaria and person_name and person_name not in kancelaria:
            display = f"{kancelaria} — {person_name}"
        elif kancelaria:
            display = kancelaria
        elif person_name:
            display = f"Radca prawny {person_name}"
        else:
            return None

        return Lead(
            name=display,
            source=self.name,
            nameMore="Radca prawny / prawo rodzinne",
            city=city,
            street=street,
            postalCode=postal,
            email=email,
            phone=phone,
            extra={"profile_url": url},
        )

    def _parse_detail_http(self, url: str) -> Lead | None:
        """Tryb seed_urls — bezpośrednie pobranie profilu (nie listing)."""
        r = self.get(url)
        if r is None:
            return None
        soup = BeautifulSoup(r.text, "lxml")
        h1 = soup.find("h1")
        title = h1.get_text(" ", strip=True) if h1 else None
        text = soup.get_text("\n", strip=True)
        return self._lead_from_text(url, title, text)

    # ---------- Apify ----------
    def _actor_path(self) -> str:
        return self.actor.replace("/", "~")

    def _build_apify_input(self) -> dict:
        start_urls = [
            {"url": f"{SEARCH_URL}?query={self.query.replace(' ', '+')}&type=t&page={p}"}
            for p in range(1, self.max_pages + 1)
        ]
        return {
            "startUrls": start_urls,
            "pseudoUrls": [
                {"purl": f"{BASE}/lawyers/[.*]"},
            ],
            "linkSelector": 'a[href*="/lawyers/"]',
            "pageFunction": _PAGE_FUNCTION,
            "maxRequestsPerCrawl": self.max_items + self.max_pages + 5,
            "maxPagesPerCrawl": self.max_items + self.max_pages + 5,
            "proxyConfiguration": {"useApifyProxy": True},
            "runMode": "PRODUCTION",
        }

    def _run_apify(self) -> list[dict]:
        url = f"{APIFY_BASE}/acts/{self._actor_path()}/run-sync-get-dataset-items"
        params = {"token": self.api_key, "timeout": config.APIFY_RUN_TIMEOUT}
        payload = self._build_apify_input()
        log.info("[oirp] Uruchamiam Apify aktora %s (max_items=%d, pages=%d)",
                 self.actor, self.max_items, self.max_pages)
        try:
            r = requests.post(url, params=params, json=payload,
                              timeout=config.APIFY_RUN_TIMEOUT + 30)
        except requests.RequestException as e:
            log.error("[oirp] Apify network error: %s", e)
            return []
        if r.status_code >= 400:
            log.error("[oirp] Apify HTTP %d: %s", r.status_code, r.text[:500])
            if is_billing_error(r.text, r.status_code):
                notify_billing_error(
                    "oirp",
                    f"Apify HTTP {r.status_code}: {r.text[:300]}",
                )
            return []
        try:
            return r.json()
        except ValueError as e:
            log.error("[oirp] Apify invalid JSON: %s", e)
            return []

    # ---------- main loop ----------
    def fetch(self) -> Iterator[Lead]:
        yielded = 0
        seen: set[str] = set()

        # Tryb 1: seed URL-i (np. ręczna lista profili)
        if self.seed_urls:
            for url in self.seed_urls:
                if self.limit and yielded >= self.limit:
                    return
                if url in seen:
                    continue
                seen.add(url)
                lead = self._parse_detail_http(url)
                if not lead or not lead.is_valid():
                    continue
                yielded += 1
                yield lead
            return

        # Tryb 2: Apify
        if not self.api_key:
            log.warning("[oirp] Brak APIFY_API_KEY i brak seed_urls — pomijam źródło "
                        "(szukajradcy.pl/search jest zablokowany przez robots.txt).")
            return

        items = self._run_apify()
        log.info("[oirp] Apify zwrócił %d rekordów", len(items))

        for item in items:
            if self.limit and yielded >= self.limit:
                return
            if not isinstance(item, dict) or item.get("type") != "profile":
                continue
            url = item.get("url") or ""
            if url in seen:
                continue
            seen.add(url)
            lead = self._lead_from_text(url, item.get("title"), item.get("text") or "")
            if not lead or not lead.is_valid():
                continue
            yielded += 1
            yield lead
