"""Bazowa klasa scrapera źródła leadów."""

from __future__ import annotations

import logging
import re
import time
from dataclasses import dataclass, field
from typing import Iterator
from urllib.parse import urlparse
from urllib.robotparser import RobotFileParser

import requests

from .. import config
from ..notifier import is_billing_error, notify_billing_error

log = logging.getLogger(__name__)

EMAIL_RE = re.compile(r"[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}")
PHONE_RE = re.compile(r"(?:\+?48[\s\-]?)?(?:\d[\s\-]?){9}")

_PL_TRANS = str.maketrans(
    "ąćęłńóśźżĄĆĘŁŃÓŚŹŻ",
    "acelnoszzACELNOSZZ",
)


def _make_email_slug(name: str) -> str:
    """Turn a company name into a Gmail + alias slug (max 30 chars)."""
    slug = name.translate(_PL_TRANS).lower()
    slug = re.sub(r"[^a-z0-9\s-]", "", slug)
    slug = re.sub(r"\s+", "-", slug.strip())
    slug = re.sub(r"-+", "-", slug)
    return slug[:30].rstrip("-")


@dataclass
class Lead:
    """Surowy lead ze źródła — przed konwersją do formatu CRM."""

    name: str
    source: str
    nameMore: str | None = None
    city: str | None = None
    street: str | None = None
    postalCode: str | None = None
    email: str | None = None
    phone: str | None = None
    website: str | None = None
    extra: dict = field(default_factory=dict)

    def is_valid(self) -> bool:
        """Lead musi mieć nazwę i co najmniej email lub telefon."""
        return bool(self.name and (self.email or self.phone))

    def to_crm_payload(self) -> dict:
        from ..config import (
            DEFAULT_TAGS,
            DEFAULT_CATEGORIES,
            SOURCE_TAGS,
            SOURCE_TAG_COLORS,
            SOURCE_TAG_COLOR_DEFAULT,
            TEST_MODE,
            TEST_EMAIL,
        )

        if TEST_MODE:
            slug = _make_email_slug(self.name)
            _local, _domain = TEST_EMAIL.split("@", 1)
            test_email = f"{_local}+{slug}@{_domain}"
            log.info(
                "[%s] [TEST MODE] email nadpisany na %s (oryginał: %s)",
                self.source, test_email, self.email or "-",
            )
            self.email = test_email

        tags = list(DEFAULT_TAGS)
        src_tag = SOURCE_TAGS.get(self.source)
        if src_tag:
            color = SOURCE_TAG_COLORS.get(self.source, SOURCE_TAG_COLOR_DEFAULT)
            tags.append({"tag": src_tag, "color": color})

        payload = {
            "name": self.name,
            "tags": tags,
            "categories": list(DEFAULT_CATEGORIES),
        }
        for key in ("nameMore", "city", "street", "postalCode", "email", "phone"):
            val = getattr(self, key)
            if val:
                payload[key] = val
        return payload


class ScraperSource:
    """Bazowa klasa wszystkich scraperów."""

    name: str = "base"

    def __init__(self, limit: int | None = None):
        self.limit = limit
        self.session = requests.Session()
        self.session.headers.update(config.DEFAULT_HEADERS)
        self._robots: dict[str, RobotFileParser] = {}

    # ---------- helpers ----------
    def get(self, url: str, **kwargs) -> requests.Response | None:
        if not self._robots_allowed(url):
            log.warning("[%s] robots.txt disallows %s", self.name, url)
            return None
        try:
            r = self.session.get(url, timeout=config.REQUEST_TIMEOUT, **kwargs)
            time.sleep(config.DELAY_SECONDS)
            if r.status_code >= 400:
                log.warning("[%s] HTTP %d for %s", self.name, r.status_code, url)
                if is_billing_error(r.text, r.status_code):
                    notify_billing_error(
                        self.name,
                        f"HTTP {r.status_code} dla {url}: {r.text[:300]}",
                    )
                return None
            return r
        except requests.RequestException as e:
            log.warning("[%s] request error for %s: %s", self.name, url, e)
            return None

    def _robots_allowed(self, url: str) -> bool:
        try:
            parsed = urlparse(url)
            origin = f"{parsed.scheme}://{parsed.netloc}"
            rp = self._robots.get(origin)
            if rp is None:
                rp = RobotFileParser()
                rp.set_url(f"{origin}/robots.txt")
                try:
                    rp.read()
                except Exception:
                    # brak robots.txt → zakładamy "allow"
                    rp = None
                self._robots[origin] = rp
            if rp is None:
                return True
            return rp.can_fetch(config.USER_AGENT, url)
        except Exception:
            return True

    @staticmethod
    def extract_email(text: str) -> str | None:
        m = EMAIL_RE.search(text or "")
        return m.group(0) if m else None

    @staticmethod
    def extract_phone(text: str) -> str | None:
        m = PHONE_RE.search(text or "")
        if not m:
            return None
        return re.sub(r"[\s\-]", "", m.group(0))

    # ---------- interfejs do nadpisania ----------
    def fetch(self) -> Iterator[Lead]:
        """Generator leadów. Każda podklasa musi to zaimplementować."""
        raise NotImplementedError
