"""Scraper Google Places API (textsearch).

Wymaga klucza w `config.GOOGLE_MAPS_API_KEY`. Bez klucza scraper jest no-opem
i loguje ostrzeżenie. Email pobierany jest opcjonalnie ze strony www firmy
prostym regexem.
"""

from __future__ import annotations

import logging
import re
import time
from typing import Iterator

import requests

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

log = logging.getLogger(__name__)

PLACES_SEARCH = "https://maps.googleapis.com/maps/api/place/textsearch/json"
PLACES_DETAILS = "https://maps.googleapis.com/maps/api/place/details/json"


class GoogleMapsScraper(ScraperSource):
    name = "google_maps"

    def __init__(
        self,
        limit: int | None = None,
        cities: list[str] | None = None,
        api_key: str | None = None,
    ):
        super().__init__(limit=limit)
        self.cities = cities or config.CITIES
        self.api_key = api_key or config.GOOGLE_MAPS_API_KEY

    def _query_places(self, query: str) -> list[dict]:
        try:
            r = self.session.get(
                PLACES_SEARCH,
                params={"query": query, "key": self.api_key, "language": "pl"},
                timeout=config.REQUEST_TIMEOUT,
            )
            time.sleep(config.DELAY_SECONDS)
        except requests.RequestException as e:
            log.warning("[google_maps] HTTP error for %s: %s", query, e)
            return []
        if r.status_code != 200:
            log.warning("[google_maps] HTTP %d for %s", r.status_code, query)
            if is_billing_error(r.text, r.status_code):
                notify_billing_error(
                    "google_maps",
                    f"HTTP {r.status_code} dla query={query!r}: {r.text[:300]}",
                )
            return []
        data = r.json()
        status = data.get("status")
        if status not in ("OK", "ZERO_RESULTS"):
            err_msg = data.get("error_message") or ""
            log.warning("[google_maps] API status=%s msg=%s", status, err_msg)
            # Status Google Places związane z billingiem / quota
            billing_statuses = {
                "OVER_QUERY_LIMIT", "OVER_DAILY_LIMIT",
                "REQUEST_DENIED", "RESOURCE_EXHAUSTED",
            }
            if status in billing_statuses or is_billing_error(f"{status} {err_msg}"):
                notify_billing_error(
                    "google_maps",
                    f"Places API status={status}, error_message={err_msg!r}",
                )
            return []
        return data.get("results", []) or []

    def _place_details(self, place_id: str) -> dict:
        try:
            r = self.session.get(
                PLACES_DETAILS,
                params={
                    "place_id": place_id,
                    "fields": "name,formatted_address,formatted_phone_number,website,address_components",
                    "key": self.api_key,
                    "language": "pl",
                },
                timeout=config.REQUEST_TIMEOUT,
            )
            time.sleep(config.DELAY_SECONDS)
        except requests.RequestException as e:
            log.warning("[google_maps] details error: %s", e)
            return {}
        if r.status_code != 200:
            return {}
        return (r.json() or {}).get("result", {}) or {}

    def _scrape_email_from_site(self, website: str) -> str | None:
        if not website:
            return None
        try:
            r = self.session.get(website, timeout=config.REQUEST_TIMEOUT)
        except requests.RequestException:
            return None
        if r.status_code != 200:
            return None
        return self.extract_email(r.text)

    def _to_lead(self, details: dict) -> Lead | None:
        name = details.get("name")
        if not name:
            return None
        phone = details.get("formatted_phone_number")
        if phone:
            phone = re.sub(r"[\s\-]", "", phone)
        website = details.get("website")
        email = self._scrape_email_from_site(website) if website else None

        # Wyciągnij miasto, kod pocztowy, ulicę z address_components
        city = postal = street = None
        for comp in details.get("address_components") or []:
            types = comp.get("types") or []
            if "locality" in types:
                city = comp.get("long_name")
            elif "postal_code" in types:
                postal = comp.get("long_name")
            elif "route" in types:
                street = comp.get("long_name")
        if street:
            for comp in details.get("address_components") or []:
                if "street_number" in (comp.get("types") or []):
                    street = f"{street} {comp.get('long_name')}"

        return Lead(
            name=name,
            source=self.name,
            nameMore="Kancelaria / Google Maps",
            city=city,
            street=street,
            postalCode=postal,
            email=email,
            phone=phone,
            website=website,
        )

    def fetch(self) -> Iterator[Lead]:
        if not self.api_key:
            log.warning("[google_maps] Brak GOOGLE_MAPS_API_KEY — pomijam źródło.")
            return

        yielded = 0
        seen: set[str] = set()
        for city in self.cities:
            if self.limit and yielded >= self.limit:
                return
            query = f"kancelaria adwokacka prawo rodzinne {city}"
            results = self._query_places(query)
            log.info("[google_maps] %s → %d places", city, len(results))
            for place in results:
                if self.limit and yielded >= self.limit:
                    return
                pid = place.get("place_id")
                if not pid or pid in seen:
                    continue
                seen.add(pid)
                details = self._place_details(pid)
                lead = self._to_lead(details)
                if lead and lead.is_valid():
                    yielded += 1
                    yield lead
