"""
Base scraper class with shared HTTP logic, retry handling, and rate limiting.
"""
import time
import logging
from abc import ABC, abstractmethod
from datetime import date
from typing import List

import requests

from models import EPGEntry
import config

logger = logging.getLogger(__name__)


class BaseScraper(ABC):
    """Abstract base class for EPG scrapers."""

    def __init__(self):
        self.session = requests.Session()
        self.session.headers.update({
            "User-Agent": config.USER_AGENT,
            "Accept": "application/json",
        })
        self._last_request_time = 0
        self._min_request_interval = 0.5  # seconds between requests

    @abstractmethod
    def get_provider_name(self) -> str:
        """Return the provider name (e.g., 'rai', 'mediaset', 'sky')."""
        ...

    @abstractmethod
    def get_channels(self) -> dict:
        """Return dict of channel_id -> display_name."""
        ...

    @abstractmethod
    def fetch_channel_schedule(self, channel_id: str, channel_name: str,
                                target_date: date) -> List[EPGEntry]:
        """Fetch schedule for a single channel on a given date."""
        ...

    def fetch_all_channels(self, target_date: date) -> List[EPGEntry]:
        """Fetch schedule for all channels of this provider."""
        all_entries = []
        channels = self.get_channels()

        for channel_id, channel_name in channels.items():
            try:
                entries = self.fetch_channel_schedule(
                    channel_id, channel_name, target_date
                )
                all_entries.extend(entries)
                logger.info(
                    f"[{self.get_provider_name()}] {channel_name}: "
                    f"{len(entries)} programmi trovati"
                )
            except Exception as e:
                logger.error(
                    f"[{self.get_provider_name()}] Errore per {channel_name}: {e}"
                )
                continue

        return all_entries

    def _make_request(self, url: str) -> dict:
        """
        Make an HTTP GET request with retry logic and rate limiting.
        Returns parsed JSON response.
        """
        # Rate limiting
        elapsed = time.time() - self._last_request_time
        if elapsed < self._min_request_interval:
            time.sleep(self._min_request_interval - elapsed)

        last_error = None
        for attempt in range(1, config.MAX_RETRIES + 1):
            try:
                self._last_request_time = time.time()
                response = self.session.get(
                    url, timeout=config.REQUEST_TIMEOUT
                )
                response.raise_for_status()
                return response.json()
            except requests.exceptions.HTTPError as e:
                last_error = e
                logger.warning(
                    f"[{self.get_provider_name()}] HTTP {response.status_code} "
                    f"(tentativo {attempt}/{config.MAX_RETRIES}): {url}"
                )
            except requests.exceptions.RequestException as e:
                last_error = e
                logger.warning(
                    f"[{self.get_provider_name()}] Errore di rete "
                    f"(tentativo {attempt}/{config.MAX_RETRIES}): {e}"
                )
            except ValueError as e:
                last_error = e
                logger.warning(
                    f"[{self.get_provider_name()}] Errore parsing JSON "
                    f"(tentativo {attempt}/{config.MAX_RETRIES}): {e}"
                )

            if attempt < config.MAX_RETRIES:
                time.sleep(config.RETRY_DELAY * attempt)

        raise ConnectionError(
            f"[{self.get_provider_name()}] Tutti i {config.MAX_RETRIES} "
            f"tentativi falliti per {url}: {last_error}"
        )
