"""Wysyłka alertów przez SendGrid (HTTPS API, bez SMTP).

Używane głównie do alertów o błędach billing/quota w zewnętrznych API
(Google Places, Apify itd.). Każda funkcja jest fail-safe — jeśli SendGrid
nie odpowie albo brak konfiguracji, log się pojawi, ale scraper nie crashuje.
"""

from __future__ import annotations

import logging
import re
import socket
from datetime import datetime

from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail

from . import config

log = logging.getLogger(__name__)

# Słowa-klucze sygnalizujące błąd billingowy / wyczerpanie quota.
# Łapiemy zarówno polskie jak i angielskie warianty z różnych dostawców
# (Apify: "credit", "monthly usage"; Google: "OVER_QUERY_LIMIT", "billing not enabled").
_BILLING_PATTERNS = re.compile(
    r"\b("
    r"billing|quota|payment[\s_]*required|"
    r"insufficient[\s_]*(funds|credit|balance)|"
    r"out[\s_]*of[\s_]*credit|credit[\s_]*exhausted|"
    r"over[\s_]*query[\s_]*limit|over[\s_]*daily[\s_]*limit|"
    r"request[\s_]*denied|monthly[\s_]*usage|"
    r"subscription|trial[\s_]*expired|plan[\s_]*limit|"
    r"usage[\s_]*limit|exceeded|"
    r"rate[\s_]*limit|too[\s_]*many[\s_]*requests"
    r")\b",
    re.IGNORECASE,
)

# Deduplikacja w obrębie jednego procesu — nie spamuj jeśli ten sam błąd
# leci 50 razy w pętli.
_sent_keys: set[str] = set()


def is_billing_error(text: str | None, status_code: int | None = None) -> bool:
    """Heurystyka: czy ten komunikat / status wygląda na błąd billing/quota."""
    if status_code in (402, 429):
        return True
    if not text:
        return False
    return bool(_BILLING_PATTERNS.search(text))


def send_alert(subject: str, body: str) -> bool:
    """Wyślij maila przez SendGrid API. Zwraca True jeśli wysłano."""
    api_key = getattr(config, "SENDGRID_API_KEY", None)
    to_addr = getattr(config, "ALERT_EMAIL", None)
    from_addr = getattr(config, "SENDGRID_FROM", None)
    if not (api_key and to_addr and from_addr):
        log.warning("[notifier] SendGrid nie jest skonfigurowany — pomijam alert.")
        return False

    message = Mail(
        from_email=from_addr,
        to_emails=to_addr,
        subject=subject,
        plain_text_content=body,
    )
    try:
        sg = SendGridAPIClient(api_key)
        resp = sg.send(message)
        log.info("[notifier] alert wysłany (HTTP %s): %s", resp.status_code, subject)
        return 200 <= int(resp.status_code) < 300
    except Exception as e:
        log.error("[notifier] błąd SendGrid: %s", e)
        return False


def notify_billing_error(source: str, error: str) -> None:
    """Wyślij ujednolicony alert o błędzie billing/quota dla danego źródła.

    Deduplikuje per (źródło, treść błędu) w obrębie procesu, żeby nie zalać
    skrzynki gdy pętla scrapera trafia na ten sam błąd wielokrotnie.
    """
    key = f"{source}:{(error or '')[:200]}"
    if key in _sent_keys:
        log.debug("[notifier] alert dla %s już wysłany w tej sesji — pomijam.", source)
        return
    _sent_keys.add(key)

    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    try:
        host = socket.gethostname()
    except Exception:
        host = "unknown"

    subject = f"⛔ BILLING ERROR — {source} | scraper-prawny.klono.ai"
    body = (
        f"Źródło: {source}\n"
        f"Błąd: {error}\n"
        f"Timestamp: {timestamp}\n"
        f"Serwer: {host} (scraper-prawny.klono.ai)\n"
        f"\n"
        f"Sprawdź konto / billing zewnętrznego API i wznów scrapera.\n"
    )
    send_alert(subject, body)
