"""
Configuration management pour IPWatch
Charge et valide le fichier config.yaml
"""
import yaml
from pathlib import Path
from typing import Dict, Any, List, Optional
from pydantic import BaseModel, Field


class AppConfig(BaseModel):
    """Configuration de l'application"""
    name: str = "IPWatch"
    version: str = "1.0.0"
    debug: bool = False


class NetworkConfig(BaseModel):
    """Configuration réseau"""
    cidr: str
    gateway: Optional[str] = None
    dns: Optional[List[str]] = None


class ScanConfig(BaseModel):
    """Configuration des scans"""
    ping_interval: int = 60  # secondes
    ping_count: int = 1  # Nombre de ping par IP
    port_scan_interval: int = 300  # secondes
    parallel_pings: int = 50
    timeout: float = 1.0
    force_vendor_update: bool = False


class PortsConfig(BaseModel):
    """Configuration des ports à scanner"""
    ranges: List[str] = ["22", "80", "443", "3389", "8080"]
    protocols: Optional[Dict[int, str]] = None  # Mapping port -> protocole


class HistoryConfig(BaseModel):
    """Configuration de l'historique"""
    retention_hours: int = 24


class UIConfig(BaseModel):
    """Configuration UI"""
    offline_transparency: float = 0.5
    show_mac: bool = True
    show_vendor: bool = True
    cell_size: int = 30
    font_size: int = 10
    cell_gap: float = 2
    details_font_size: int = 13
    details_spacing: int = 2
    architecture_title_font_size: int = 18


class LinksConfig(BaseModel):
    """Configuration des liens"""
    hardware_bench_url: Optional[str] = None


class ColorsConfig(BaseModel):
    """Configuration des couleurs"""
    free: str = "#75715E"
    online_known: str = "#A6E22E"
    online_unknown: str = "#66D9EF"
    offline_known: str = "#F92672"
    offline_unknown: str = "#AE81FF"
    mac_changed: str = "#FD971F"
    network_device: str = "#1E3A8A"


class OPNsenseConfig(BaseModel):
    """Configuration OPNsense API"""
    enabled: bool = False
    host: str = ""
    api_key: str = ""
    api_secret: str = ""
    verify_ssl: bool = False
    protocol: str = "http"  # "http" ou "https"


class ServiceDefinition(BaseModel):
    """Définition d'un service réseau (pour scan de services)"""
    name: str
    port: int
    protocol: Optional[str] = None  # http, https, ssh, rdp, smb...
    description: str = ""


class DatabaseConfig(BaseModel):
    """Configuration base de données"""
    path: str = "./data/db.sqlite"


class SubnetConfig(BaseModel):
    """Configuration d'un sous-réseau"""
    name: str
    cidr: str
    start: str
    end: str
    description: str


class HostConfig(BaseModel):
    """Configuration d'un hôte avec sa localisation"""
    name: str
    location: str
    ip: Optional[str] = None
    ip_parent: Optional[str] = None
    ip_enfant: Optional[List[str]] = None


class IPWatchConfig(BaseModel):
    """Configuration complète IPWatch"""
    model_config = {"arbitrary_types_allowed": True}

    app: AppConfig = Field(default_factory=AppConfig)
    network: NetworkConfig
    subnets: List[SubnetConfig] = Field(default_factory=list)
    ip_classes: Dict[str, Any] = Field(default_factory=dict)
    scan: ScanConfig = Field(default_factory=ScanConfig)
    ports: PortsConfig = Field(default_factory=PortsConfig)
    locations: List[str] = Field(default_factory=list)
    hosts: List[HostConfig] = Field(default_factory=list)
    history: HistoryConfig = Field(default_factory=HistoryConfig)
    ui: UIConfig = Field(default_factory=UIConfig)
    links: LinksConfig = Field(default_factory=LinksConfig)
    colors: ColorsConfig = Field(default_factory=ColorsConfig)
    database: DatabaseConfig = Field(default_factory=DatabaseConfig)
    opnsense: OPNsenseConfig = Field(default_factory=OPNsenseConfig)
    services: List[ServiceDefinition] = Field(default_factory=list)


class ConfigManager:
    """Gestionnaire de configuration singleton"""
    _instance: Optional['ConfigManager'] = None
    _config: Optional[IPWatchConfig] = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def load_config(self, config_path: str = "./config.yaml") -> IPWatchConfig:
        """Charge la configuration depuis le fichier YAML"""
        path = Path(config_path)

        if not path.exists():
            raise FileNotFoundError(f"Fichier de configuration non trouvé: {config_path}")

        with open(path, 'r', encoding='utf-8') as f:
            yaml_data = yaml.safe_load(f)

        self._config = IPWatchConfig(**yaml_data)
        self._config_path = config_path
        return self._config

    def reload_config(self) -> IPWatchConfig:
        """Recharge la configuration depuis le fichier"""
        if not hasattr(self, '_config_path'):
            self._config_path = "./config.yaml"
        return self.load_config(self._config_path)

    @property
    def config(self) -> IPWatchConfig:
        """Retourne la configuration actuelle"""
        if self._config is None:
            raise RuntimeError("Configuration non chargée. Appelez load_config() d'abord.")
        return self._config


# Instance globale
config_manager = ConfigManager()
