__all__ = ['config']

import json
import os
from pathlib import Path
import socket
from typing import Any, List, Optional, Union

import dpath
import yaml


def _load_document(stem: Path) -> Any | None:
    yaml_path = stem.with_suffix('.yaml')
    json_path = stem.with_suffix('.json')
    if yaml_path.exists():
        with yaml_path.open('r', encoding='utf-8') as f:
            return yaml.safe_load(f)
    if json_path.exists():
        with json_path.open('r', encoding='utf-8') as f:
            return json.load(f)


class _ConfigLoader:
    """
    Singleton configuration loader that manages loading and caching of configurations.
    
    This class implements the singleton pattern to ensure only one instance exists.
    """
    _instance = None  # Class-level singleton instance
    _initialized = False  # Track if initialization has occurred

    def __new__(cls, *args, **kwargs):
        """Singleton pattern - ensure only one instance exists"""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, base_dir: Optional[Union[str, Path]] = None):
        """
        Initialize the configuration loader.
        Only runs once due to singleton pattern.
        
        Args:
            base_dir: Base directory for configurations. If None, uses INCEPTUM
                     environment variable or ~/.config/inceptum
        """
        if self._initialized:
            return
        
        if base_dir is None:
            base_dir = os.environ.get("INCEPTUM", "~/.config/inceptum")
        
        self.base_dir = Path(base_dir).expanduser()
        self._dirs = []
        self._document_cache = {}
        self._loaded = False
        self._initialized = True

    def reset(self) -> None:
        """Reset the loader, clearing all cached configurations."""
        self._loaded = False
        self._document_cache.clear()
        # Keep base_dir and other settings

    @classmethod
    def reset_singleton(cls):
        """Reset the singleton instance, allowing a new instance to be created."""
        cls._instance = None
        cls._initialized = False
    
    @classmethod
    def get_global_instance(cls):
        """Get the global singleton instance for backward compatibility."""
        return cls()
    
    @classmethod
    def set_global_instance(cls, value):
        """Set the global singleton instance for backward compatibility."""
        if value is None:
            cls.reset_singleton()

    def _ensure_loaded(self) -> None:
        if self._loaded:
            return

        root = self._document_cache['config'] = _load_document(self.base_dir / "config") or {}
        if not isinstance(root, dict):
            raise TypeError("Root config must be a mapping (dict-like)")

        before = root.get("before", [])
        if not isinstance(before, list):
            raise TypeError('Root key "before" must be a list of directories')

        after = root.get("after", [])
        if not isinstance(after, list):
            raise TypeError('Root key "after" must be a list of directories')

        self._dirs = [Path(p).expanduser() for p in before] + [self.base_dir] + [Path(p).expanduser() for p in after]
        self._loaded = True

    def get(self, *args, default=None):
        self._ensure_loaded()
        path = [p for arg in args for p in arg.split('.')]
        if path[0] not in self._document_cache:
            merged = None
            for d in self._dirs:
                if document := _load_document(d / path[0]):
                    merged = dpath.merge(merged, document) if merged else document
            if merged is not None:
                self._document_cache[path[0]] = merged
        return dpath.get(self._document_cache, path, default=default)


# Global loader instance - this is the main interface
_loader: _ConfigLoader | None = None

def config(*args, **kwargs) -> Any:
    """
    Get a configuration value using the global loader.
    
    This function uses the global _loader instance.
    """
    global _loader
    if _loader is None:
        # Reset the singleton when creating a new loader
        _ConfigLoader.reset_singleton()
        _loader = _ConfigLoader()  # This will return a fresh singleton instance
    return _loader.get(*args, **kwargs)
