"""
Configuration management for rateloader.

Handles reading/writing settings.ini with all required sections and keys
as specified in PRD §4. Auto-creates settings.ini with defaults if missing
and auto-appends missing keys with defaults if settings.ini exists but lacks keys.
"""

import os
import configparser
from typing import Dict, Any, Optional


class Config:
    """Configuration manager for rateloader application."""

    def __init__(self, config_file: str = "settings.ini"):
        """
        Initialize configuration manager.

        Args:
            config_file: Path to the configuration file
        """
        self.config_file = config_file
        self.config = configparser.ConfigParser()
        self._defaults = {
            "settings": {
                # Oracle (shared)
                "oracleconnectstring": "v8live/live@v8",
                "nls_lang": "AMERICAN_AMERICA.CL8MSWIN1251",
                "file_encoding": "cp1251",
                # ExportMailer (legacy)
                "hotel_key": "HOTEL123",
                "url": "http://fidelio.pro/select.sql",
                "block-url": "http://fidelio.pro/selectBlocks.sql",
                "sendfrom": "folkote@yandex.ru",
                "sendto": "metraplex@gmail.com",
                "mailserver": "smtp.yandex.ru",
                "port": "465",
                "pwd": "hvlmwcrnbafsavjy",
                "ssl": "1",
                "compress_attachments": "0",
                "auto_update_sql": "1",
                # PriceExport (legacy)
                "api_url": "https://app.hotellab.io/privat/api/integration/1c/set-pms-prices/5",
                "token": "1111",
                "months": "12",
                "batch_max_size_kb": "1024",  # 1MB default batch size
                "batch_max_days": "30",  # Maximum days per batch
                "deeper_logging": "0",  # Enable deeper HTTP request/response logging
                # RateLoader HTTP
                "server_port": "8000",
                "auto_start_server": "1",
                # IP Management
                "ip_file": "ip_access.json",
                "auto_add_new_ips": "1",
                "log_denied_requests": "1",
                "max_tracked_ips": "1000",
                # Scheduler (fixed hours)
                "schedule_hours": "8,12,15,18,20",
                "enable_price_export_scheduling": "1",
            }
        }

        self._load_config()

    def _load_config(self) -> None:
        """Load configuration from file, creating with defaults if needed."""
        if os.path.exists(self.config_file):
            self.config.read(self.config_file, encoding="utf-8")
            self._ensure_all_keys_exist()
        else:
            self._create_default_config()

    def _create_default_config(self) -> None:
        """Create a new configuration file with default values."""
        print(
            f"{self.config_file} configuration file not found. Creating a new one with default settings."
        )

        for section_name, section_defaults in self._defaults.items():
            self.config[section_name] = section_defaults

        self._save_config()

    def _ensure_all_keys_exist(self) -> None:
        """Ensure all required keys exist in the configuration, adding missing ones with defaults."""
        config_updated = False

        for section_name, section_defaults in self._defaults.items():
            if section_name not in self.config:
                self.config[section_name] = {}
                config_updated = True

            for key, default_value in section_defaults.items():
                if key not in self.config[section_name]:
                    self.config[section_name][key] = default_value
                    config_updated = True

        if config_updated:
            self._save_config()

    def _save_config(self) -> None:
        """Save configuration to file."""
        with open(self.config_file, "w", encoding="utf-8") as f:
            self.config.write(f)

    def get(
        self, key: str, section: str = "settings", fallback: Optional[Any] = None
    ) -> str:
        """
        Get a configuration value.

        Args:
            key: Configuration key
            section: Configuration section (default: 'settings')
            fallback: Fallback value if key not found

        Returns:
            Configuration value as string
        """
        return self.config.get(section, key, fallback=fallback)

    def get_int(self, key: str, section: str = "settings", fallback: int = 0) -> int:
        """
        Get a configuration value as integer.

        Args:
            key: Configuration key
            section: Configuration section (default: 'settings')
            fallback: Fallback value if key not found

        Returns:
            Configuration value as integer
        """
        try:
            value = self.config.get(section, key, fallback=None)
            if value is None:
                return fallback
            return int(value)
        except (ValueError, configparser.NoSectionError, configparser.NoOptionError):
            return fallback

    def get_bool(
        self, key: str, section: str = "settings", fallback: bool = False
    ) -> bool:
        """
        Get a configuration value as boolean.

        Args:
            key: Configuration key
            section: Configuration section (default: 'settings')
            fallback: Fallback value if key not found

        Returns:
            Configuration value as boolean
        """
        try:
            return self.config.getboolean(section, key, fallback=fallback)
        except (ValueError, configparser.NoSectionError, configparser.NoOptionError):
            return fallback

    def get_float(
        self, key: str, section: str = "settings", fallback: float = 0.0
    ) -> float:
        """
        Get a configuration value as float.

        Args:
            key: Configuration key
            section: Configuration section (default: 'settings')
            fallback: Fallback value if key not found

        Returns:
            Configuration value as float
        """
        try:
            return self.config.getfloat(section, key, fallback=fallback)
        except (ValueError, configparser.NoSectionError, configparser.NoOptionError):
            return fallback

    def set(self, key: str, value: Any, section: str = "settings") -> None:
        """
        Set a configuration value.

        Args:
            key: Configuration key
            value: Configuration value
            section: Configuration section (default: 'settings')
        """
        if section not in self.config:
            self.config[section] = {}

        self.config[section][key] = str(value)
        self._save_config()

    def get_schedule_hours(self) -> list:
        """
        Get schedule hours as a list of integers.

        Returns:
            List of schedule hours
        """
        hours_str = self.get("schedule_hours")
        try:
            return [int(hour.strip()) for hour in hours_str.split(",")]
        except (ValueError, AttributeError):
            return [8, 12, 15, 18, 20]  # Default fallback

    def get_all_settings(self) -> Dict[str, Dict[str, str]]:
        """
        Get all configuration settings as a dictionary.

        Returns:
            Dictionary containing all configuration settings
        """
        result = {}
        for section_name in self.config.sections():
            result[section_name] = dict(self.config[section_name])
        return result


# Global configuration instance
_config = None


def get_config() -> Config:
    """
    Get the global configuration instance.

    Returns:
        Global Config instance
    """
    global _config
    if _config is None:
        _config = Config()
    return _config


def reload_config() -> Config:
    """
    Reload the global configuration instance.

    Returns:
        New Config instance
    """
    global _config
    _config = Config()
    return _config
