"""Software environment detection and initialization.

This module handles detection and setup of software-level environment variables
that are essential for sageLLM operation, such as:
- HuggingFace Hub connectivity (HF_ENDPOINT)
- Model caching directories
- Network proxies
- Logging levels
"""

from __future__ import annotations

import logging
import os
import socket
import subprocess
from pathlib import Path

logger = logging.getLogger(__name__)


class NetworkDetector:
    """Detect and manage HuggingFace Hub connectivity."""

    HF_OFFICIAL = "https://huggingface.co"
    HF_MIRROR = "https://hf-mirror.com"
    TIMEOUT = 5  # seconds

    @staticmethod
    def check_connectivity(url: str, timeout: int = TIMEOUT) -> bool:
        """Check if a URL is reachable via HTTP HEAD request.

        Args:
            url: URL to check
            timeout: Timeout in seconds

        Returns:
            True if URL is reachable, False otherwise
        """
        try:
            # Try curl with proper parameters
            cmd = ["curl", "-s", "-I", "--max-time", str(timeout), url]
            result = subprocess.run(
                cmd,
                capture_output=True,
                timeout=timeout + 2,  # Give extra time for curl to finish
            )

            # Check if curl succeeded (exit code 0)
            if result.returncode == 0:
                # Parse HTTP status from response
                response_text = result.stdout.decode("utf-8", errors="ignore")
                # Look for HTTP status line like "HTTP/1.1 200 OK"
                for line in response_text.split("\n"):
                    if line.startswith("HTTP/"):
                        # Extract status code
                        parts = line.split()
                        if len(parts) >= 2:
                            status_code = parts[1]
                            # Accept 2xx and 3xx responses
                            return status_code.startswith("2") or status_code.startswith("3")
            return False
        except (subprocess.TimeoutExpired, FileNotFoundError, Exception):
            # Fallback: try socket connection (more reliable, no curl needed)
            try:
                from urllib.parse import urlparse

                parsed = urlparse(url)
                host = parsed.netloc
                port = 443 if parsed.scheme == "https" else 80
                socket.create_connection((host, port), timeout=timeout)
                return True
            except (TimeoutError, OSError):
                return False

    @classmethod
    def detect_hf_endpoint(cls, verbose: bool = False) -> tuple[str, bool]:
        """Detect the best HuggingFace endpoint to use.

        Strategy:
        1. Check if HF_ENDPOINT already set (user preference)
        2. Try official HF endpoint (5s timeout)
        3. Fall back to mirror if official is unavailable

        Args:
            verbose: Print detection details

        Returns:
            Tuple of (endpoint_url, is_official)
        """
        # Check for existing user preference
        if existing_endpoint := os.environ.get("HF_ENDPOINT"):
            if verbose:
                print(f"✅ Using existing HF_ENDPOINT: {existing_endpoint}")
            return existing_endpoint, existing_endpoint == cls.HF_OFFICIAL

        # Try official endpoint
        if verbose:
            print(f"🔍 Testing HuggingFace official endpoint (timeout: {cls.TIMEOUT}s)...")

        if cls.check_connectivity(cls.HF_OFFICIAL, timeout=cls.TIMEOUT):
            if verbose:
                print(f"✅ Official endpoint reachable: {cls.HF_OFFICIAL}")
            return cls.HF_OFFICIAL, True
        else:
            if verbose:
                print("⚠️  Official endpoint unreachable, falling back to mirror...")
                print(f"🪞 Using mirror: {cls.HF_MIRROR}")
            return cls.HF_MIRROR, False


class EnvInitializer:
    """Initialize software-level environment variables."""

    @staticmethod
    def init_hf_endpoint(force_detect: bool = False, verbose: bool = False) -> None:
        """Initialize HF_ENDPOINT environment variable.

        Args:
            force_detect: Force network detection (slow, ~2s timeout)
            verbose: Print detection details

        Strategy:
        - If HF_ENDPOINT already set in env, keep it
        - If force_detect=False, try loading from cached env_profile.json
        - If force_detect=True or no cache, perform network detection
        """
        # Already set - don't override
        if not force_detect and os.environ.get("HF_ENDPOINT"):
            return

        # Try loading from cache first (fast path)
        if not force_detect:
            cached = EnvInitializer.load_env_profile()
            if cached:
                cached_endpoint = (
                    cached.get("environment", {}).get("Network", {}).get("HF_ENDPOINT")
                )
                if cached_endpoint and cached_endpoint != "Not set":
                    os.environ["HF_ENDPOINT"] = cached_endpoint
                    return

        # No cache or force_detect - perform network detection (slow)
        endpoint, is_official = NetworkDetector.detect_hf_endpoint(verbose=verbose)
        os.environ["HF_ENDPOINT"] = endpoint

        if verbose and endpoint:
            if is_official:
                print(f"✅ HF_ENDPOINT set to official: {endpoint}")
            else:
                print(f"⚠️  HF_ENDPOINT fallback to mirror: {endpoint}")

    @staticmethod
    def init_hf_cache_dir() -> None:
        """Ensure HF_HOME is set to a valid cache directory."""
        if os.environ.get("HF_HOME"):
            return

        # Use ~/.cache/huggingface by default
        hf_home = Path.home() / ".cache" / "huggingface"
        hf_home.mkdir(parents=True, exist_ok=True)
        os.environ["HF_HOME"] = str(hf_home)

    @staticmethod
    def init_model_cache_dir() -> None:
        """Ensure sageLLM model cache directory exists."""
        # Use ~/.cache/sagellm by default
        cache_dir = Path.home() / ".cache" / "sagellm"
        cache_dir.mkdir(parents=True, exist_ok=True)
        os.environ["SAGELLM_CACHE_DIR"] = str(cache_dir)

    @staticmethod
    def init_logging_level() -> None:
        """Initialize logging level from environment or default to WARNING."""
        if os.environ.get("LOGGING_LEVEL"):
            return

        # Default to WARNING (show important messages but not debug spam)
        os.environ["LOGGING_LEVEL"] = "WARNING"

    @staticmethod
    def init_all(verbose: bool = False, force_detect: bool = False) -> dict[str, str]:
        """Initialize all software-level environment variables.

        Args:
            verbose: Print initialization details
            force_detect: Force network detection (slow, ~2s timeout)

        Returns:
            Dictionary of initialized environment variables
        """
        env_snapshot = {}

        # Initialize each component (use cache by default, fast)
        EnvInitializer.init_hf_endpoint(force_detect=force_detect, verbose=verbose)
        env_snapshot["HF_ENDPOINT"] = os.environ.get("HF_ENDPOINT", "")

        EnvInitializer.init_hf_cache_dir()
        env_snapshot["HF_HOME"] = os.environ.get("HF_HOME", "")

        EnvInitializer.init_model_cache_dir()
        env_snapshot["SAGELLM_CACHE_DIR"] = os.environ.get("SAGELLM_CACHE_DIR", "")

        EnvInitializer.init_logging_level()
        env_snapshot["LOGGING_LEVEL"] = os.environ.get("LOGGING_LEVEL", "")

        # Save to profile file only if force_detect was used
        if force_detect:
            EnvInitializer.save_env_profile()

        if verbose:
            print("✅ Software environment initialized")

        return env_snapshot

    @staticmethod
    def get_env_info() -> dict[str, dict[str, str]]:
        """Get current software environment info for display.

        Returns:
            Dictionary with environment variable groups and their values
        """
        return {
            "Network": {
                "HF_ENDPOINT": os.environ.get("HF_ENDPOINT", "Not set"),
            },
            "Cache": {
                "HF_HOME": os.environ.get("HF_HOME", "Not set"),
                "SAGELLM_CACHE_DIR": os.environ.get("SAGELLM_CACHE_DIR", "Not set"),
            },
            "Logging": {
                "LOGGING_LEVEL": os.environ.get("LOGGING_LEVEL", "Not set"),
            },
        }

    @staticmethod
    def get_env_profile_path() -> Path:
        """Get the path to the environment profile JSON file."""
        return Path.home() / ".sagellm" / "env_profile.json"

    @staticmethod
    def save_env_profile() -> None:
        """Save current environment configuration to JSON file."""
        import json
        from datetime import datetime

        env_info = EnvInitializer.get_env_info()
        profile_path = EnvInitializer.get_env_profile_path()

        # Ensure directory exists
        profile_path.parent.mkdir(parents=True, exist_ok=True)

        # Add metadata
        profile_data = {
            "version": "1.0",
            "updated_at": datetime.now().isoformat(),
            "environment": env_info,
        }

        with open(profile_path, "w", encoding="utf-8") as f:
            json.dump(profile_data, f, indent=2, ensure_ascii=False)

    @staticmethod
    def load_env_profile() -> dict | None:
        """Load environment configuration from JSON file.

        Returns:
            Dictionary with environment config or None if not found
        """
        import json

        profile_path = EnvInitializer.get_env_profile_path()
        if not profile_path.exists():
            return None

        try:
            with open(profile_path, encoding="utf-8") as f:
                return json.load(f)
        except (json.JSONDecodeError, OSError):
            return None
