"""This module handles configuration management for the Ushka framework.

It defines the `Config` class, which uses a Singleton pattern to provide
global access to application settings. Configuration is loaded from an
'ushka.toml' file, with support for default values and automatic file
generation.
"""

import logging
import os
import threading
from importlib.metadata import version
from pathlib import Path
from secrets import token_urlsafe
from typing import Any
import tomlkit


class Config:
    """Manages application configuration using a Singleton pattern.

    This class loads settings from an 'ushka.toml' file, provides default
    values for missing keys, and makes configuration accessible throughout the
    application. The Singleton implementation ensures that there is only one
    instance of the configuration object.

    Attributes are dynamically set from the TOML file in the format
    `SECTION_KEY` (e.g., `APP_DEBUG`).
    """

    _instance = None
    _lock = threading.Lock()
    _initialized = False

    def __new__(cls, *args, **kwargs):
        """Ensures that only one instance of the Config class is created.

        This method implements the thread-safe Singleton pattern.
        """
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, workdir: Path | str = Path.cwd()) -> None:
        """Initializes the Config instance with default settings.

        This method is only executed the first time the Singleton instance is
        created. It sets up default configuration values.

        Args:
            workdir: The application's working directory.
        """
        if self._initialized:
            return

        self._defaults = {
            "ushka": {"version": version("ushka"), "workdir": str(workdir)},
            "app": {
                "name": "Ushka App",
                "debug": False,
                "auto_discover": True,
                "secret_key": token_urlsafe(128),
            },
            "server": {
                "host": "127.0.0.1",
                "port": 8000,
                "workers": 1,
                "ushka_suppress_uvicorn": True,
            },
            "static": {"enable": False, "url": "/static", "dir": "static"},
        }

        self._map_config_to_attributes(self._defaults)
        self._initialized = True
        self.log = logging.getLogger("rich")

    def load_from_file(self, config_path: Path) -> "tomlkit.TOMLDocument":
        """Loads configuration from a TOML file.

        If the specified file does not exist, it creates one with default
        settings. It merges existing settings with defaults to ensure all
        necessary configuration keys are present.

        Args:
            config_path: The path to the 'ushka.toml' configuration file.

        Returns:
            The loaded and merged configuration as a TOML document object.
        """
        if not os.path.exists(config_path):
            doc = tomlkit.document()
            doc.add(
                tomlkit.comment(
                    f"Configuration Automatically Generated by "
                    f"Ushka 🐱 v{self._defaults['ushka']['version']}"
                )
            )
            doc.update(self._defaults)

            doc.add(tomlkit.nl())

            with open(config_path, "w", encoding="utf-8") as f:
                f.write(tomlkit.dumps(doc))

            self.log.info("🔧 Ushka created a  new ushka.toml")

        with open(config_path, "r", encoding="utf-8") as f:
            doc = tomlkit.parse(f.read())

        for section, keys in self._defaults.items():
            if section == "ushka":
                doc[section] = keys

            if section not in doc:
                doc[section] = keys
            else:
                for key, value in keys.items():
                    if key not in doc[section]:
                        doc[section][key] = value

        with open(config_path, "w", encoding="utf-8") as f:
            f.write(tomlkit.dumps(doc))
            self.log.info("🔧 Ushka touched your ushka.toml")

        self._map_config_to_attributes(doc)
        return doc

    def _map_config_to_attributes(self, data: dict) -> None:
        """Maps dictionary keys to instance attributes.

        This internal method iterates through the configuration dictionary and
        sets an attribute on the `Config` instance for each key. The attribute
        name is created by combining the section and key in uppercase
        (e.g., `APP_DEBUG`).

        Args:
            data: The configuration dictionary to map.
        """
        for section, content in data.items():
            if isinstance(content, dict):
                for key, value in content.items():
                    attr_name = f"{section.upper()}_{key.upper()}"
                    setattr(self, attr_name, value)

    def get(self, key: str, default: Any = None) -> Any:
        """Retrieves a configuration value by its attribute name.

        Args:
            key: The name of the configuration attribute (e.g., 'APP_DEBUG').
            default: The value to return if the key is not found.

        Returns:
            The configuration value or the specified default.
        """
        return getattr(self, key, default)
