import json
import logging
from pathlib import Path
from typing import Any, Dict, Optional

import httpx

from db.config import settings
from db.redis_database import REDIS_SYNC_CLIENT

logger = logging.getLogger(__name__)


class RemoteConfigManager:
    CACHE_KEY = "scraper_config"
    CACHE_TTL = 3600  # Cache for 1 hour

    def __init__(self):
        self.redis_client = REDIS_SYNC_CLIENT

    def get_config(self) -> Dict[str, Any]:
        """
        Retrieve the configuration, first checking Redis cache,
        then fetching from remote or local source if necessary.
        """
        cached_config = self._get_cached_config()
        if cached_config:
            return cached_config

        try:
            config = self._fetch_config()
            self._cache_config(config)
            return config
        except Exception as e:
            logger.error(f"Error fetching config: {e}")
            return self._load_local_fallback()

    def _get_cached_config(self) -> Optional[Dict[str, Any]]:
        """Retrieve the configuration from Redis cache if available."""
        cached_config = self.redis_client.get(self.CACHE_KEY)
        if cached_config:
            return json.loads(cached_config)
        return None

    def _fetch_config(self) -> Dict[str, Any]:
        """Fetch the configuration from either remote or local source."""
        if settings.remote_config_source.startswith(("http://", "https://")):
            return self._fetch_remote_config(settings.remote_config_source)
        return self._load_local_config(settings.remote_config_source)

    def _cache_config(self, config: Dict[str, Any]) -> None:
        """Cache the configuration in Redis."""
        self.redis_client.setex(self.CACHE_KEY, self.CACHE_TTL, json.dumps(config))

    @staticmethod
    def _fetch_remote_config(config_url: str) -> Dict[str, Any]:
        """Fetch the configuration from a remote URL."""
        try:
            response = httpx.get(config_url, timeout=10)
            response.raise_for_status()
            return response.json()
        except (httpx.HTTPError, httpx.RequestError) as e:
            logger.error(f"HTTP error occurred while fetching remote config: {e}")
            raise
        except json.JSONDecodeError as e:
            logger.error(f"Invalid JSON in remote config: {e}")
            raise

    @staticmethod
    def _load_local_config(config_path: str) -> Dict[str, Any]:
        """Load the configuration from a local file."""
        path = Path(config_path)
        if not path.is_file():
            raise FileNotFoundError(f"Config file not found: {path}")
        try:
            with path.open("r") as f:
                return json.load(f)
        except json.JSONDecodeError as e:
            logger.error(f"Invalid JSON in local config file: {e}")
            raise

    def _load_local_fallback(self) -> Dict[str, Any]:
        """Load the local fallback configuration."""
        try:
            return self._load_local_config(settings.local_config_path)
        except Exception as e:
            logger.error(f"Failed to load local fallback config: {e}")
            return {}

    def get_start_url(self, spider_name: str) -> Optional[str]:
        """Get the start URL for a specific spider."""
        config = self.get_config()
        return config.get("start_urls", {}).get(spider_name)

    def get_scraper_config(self, site_name: str, key: str) -> Any:
        """Get a specific configuration for a scraper."""
        config = self.get_config()
        return config.get(site_name, {}).get(key)


config_manager = RemoteConfigManager()
