"""
DSS Configuration Module
========================

Handles configuration management for the Design System Server (DSS) Claude Plugin.
Supports local/remote mode detection, persistent configuration storage, and
environment variable overrides.
"""

import os
import json
import uuid
import asyncio
import logging
from enum import Enum
from pathlib import Path
from typing import Optional, Union, Any

import aiohttp
from pydantic import BaseModel, Field, HttpUrl, ValidationError

# Configure module-level logger
logger = logging.getLogger(__name__)

CONFIG_DIR = Path.home() / ".dss"
CONFIG_FILE = CONFIG_DIR / "config.json"
DEFAULT_REMOTE_URL = "https://dss.overbits.luz.uy"
DEFAULT_LOCAL_URL = "http://localhost:6006"


class DSSMode(str, Enum):
    """Operation modes for the DSS plugin."""
    LOCAL = "local"
    REMOTE = "remote"
    AUTO = "auto"


class DSSConfig(BaseModel):
    """
    Configuration model for DSS Plugin.

    Attributes:
        mode (DSSMode): The configured operation mode (default: AUTO).
        remote_url (str): URL for the remote DSS API.
        local_url (str): URL for the local DSS API (usually localhost).
        session_id (str): Unique identifier for this client instance.
    """
    mode: DSSMode = Field(default=DSSMode.AUTO, description="Operation mode preference")
    remote_url: str = Field(default=DEFAULT_REMOTE_URL, description="Remote API endpoint")
    local_url: str = Field(default=DEFAULT_LOCAL_URL, description="Local API endpoint")
    session_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Persistent session ID")

    class Config:
        validate_assignment = True
        extra = "ignore"  # Allow forward compatibility with new config keys

    @classmethod
    def load(cls) -> "DSSConfig":
        """
        Load configuration from ~/.dss/config.json.
        Returns a default instance if the file does not exist or is invalid.
        """
        if not CONFIG_FILE.exists():
            logger.debug(f"No config found at {CONFIG_FILE}, using defaults.")
            return cls()

        try:
            content = CONFIG_FILE.read_text(encoding="utf-8")
            data = json.loads(content)
            # Ensure complex types are handled by Pydantic validation
            return cls.model_validate(data)
        except (json.JSONDecodeError, ValidationError) as e:
            logger.warning(f"Failed to load config from {CONFIG_FILE}: {e}. Using defaults.")
            return cls()
        except Exception as e:
            logger.error(f"Unexpected error loading config: {e}")
            return cls()

    def save(self) -> None:
        """
        Save the current configuration to ~/.dss/config.json.
        Creates the directory if it does not exist.
        """
        try:
            CONFIG_DIR.mkdir(parents=True, exist_ok=True)

            # Export using mode='json' to handle enums and urls correctly
            json_data = self.model_dump_json(indent=2)
            CONFIG_FILE.write_text(json_data, encoding="utf-8")
            logger.debug(f"Configuration saved to {CONFIG_FILE}")
        except Exception as e:
            logger.error(f"Failed to save config to {CONFIG_FILE}: {e}")
            raise

    async def get_active_mode(self) -> DSSMode:
        """
        Determine the actual runtime mode based on priority rules.

        Priority:
        1. DSS_MODE environment variable
        2. Configured 'mode' (if not AUTO)
        3. Auto-detection (ping local health endpoint)
        4. Fallback to REMOTE

        Returns:
            DSSMode: The resolved active mode (LOCAL or REMOTE).
        """
        # 1. Check Environment Variable
        env_mode = os.getenv("DSS_MODE")
        if env_mode:
            try:
                # Normalize string to enum
                return DSSMode(env_mode.lower())
            except ValueError:
                logger.warning(f"Invalid DSS_MODE env var '{env_mode}', ignoring.")

        # 2. Check Configuration (if explicit)
        if self.mode != DSSMode.AUTO:
            return self.mode

        # 3. Auto-detect
        logger.info("Auto-detecting DSS mode...")
        is_local_healthy = await self._check_local_health()

        if is_local_healthy:
            logger.info(f"Local server detected at {self.local_url}. Switching to LOCAL mode.")
            return DSSMode.LOCAL
        else:
            logger.info("Local server unreachable. Fallback to REMOTE mode.")
            # 4. Fallback
            return DSSMode.REMOTE

    async def _check_local_health(self) -> bool:
        """
        Ping the local server health endpoint to check availability.

        Returns:
            bool: True if server responds with 200 OK, False otherwise.
        """
        health_url = f"{self.local_url.rstrip('/')}/health"
        try:
            timeout = aiohttp.ClientTimeout(total=2.0)  # Short timeout for responsiveness
            async with aiohttp.ClientSession(timeout=timeout) as session:
                async with session.get(health_url) as response:
                    if response.status == 200:
                        return True
                    logger.debug(f"Local health check returned status {response.status}")
        except aiohttp.ClientError as e:
            logger.debug(f"Local health check connection failed: {e}")
        except Exception as e:
            logger.debug(f"Unexpected error during health check: {e}")

        return False

    def get_api_url(self, active_mode: DSSMode) -> str:
        """
        Helper to get the correct API URL for the determined mode.
        """
        if active_mode == DSSMode.LOCAL:
            return self.local_url
        return self.remote_url
