# (c) 2015-2023 Acellera Ltd http://www.acellera.com
# All Rights Reserved
# Distributed under HTMD Software License Agreement
# No redistribution in whole or part
#
"""Centralized configuration from environment variables."""

import json
import os
from dataclasses import dataclass, field
from typing import Optional
import logging

logger = logging.getLogger(__name__)


@dataclass
class _Config:
    """Configuration loaded from environment variables.

    Attributes
    ----------
    registries : str or None
        Comma-separated list of registry URIs (PM_REGISTRIES) in priority order.
        Examples: "local:/path/to/apps,http://backend.com,docker://"
    executor : str or None
        Executor to use for running apps (PM_EXECUTOR).
        Examples: "local", "slurm", "http://backend.com"
    runtime : str
        Container runtime for local execution (PM_RUNTIME).
        Options: "docker", "apptainer". Defaults to "docker".
    queue_config : dict or None
        Queue configuration (PM_QUEUE_CONFIG) parsed from JSON
    symlink : bool
        Whether to use symlinks for input files (PM_SYMLINK)
    blocking : bool
        Whether to run jobs in blocking mode (PM_BLOCKING)
    backend_headers : dict
        HTTP headers for backend requests (PM_BACKEND_HEADERS)
    job_dir_prefix : str
        Prefix for job directories (PM_JOB_DIR_PREFIX)
    working_dir : str
        Working directory for relative paths (PM_WORKING_DIR)
    sif_cache_dir : str
        Directory for caching SIF files (PM_SIF_CACHE_DIR)
    cookie_cache_dir : str
        Directory for caching cookies (PM_COOKIE_CACHE_DIR)
    no_default_registries: bool
        Whether to not use default registries (PM_NO_DEFAULT_REGISTRIES)
    """

    registries: Optional[str] = None
    executor: Optional[str] = None
    runtime: str = "docker"
    queue_config: Optional[dict] = field(default=None)
    symlink: bool = False
    blocking: bool = False
    backend_headers: dict = field(default_factory=dict)
    job_dir_prefix: str = ""
    working_dir: Optional[str] = None
    sif_cache_dir: Optional[str] = None
    cookie_cache_dir: Optional[str] = None
    no_default_registries: bool = False


def _load_config() -> _Config:
    """Load configuration from environment variables.

    Returns
    -------
    _Config
        Configuration object with values from environment
    """
    no_default_registries = os.environ.get("PM_NO_DEFAULT_REGISTRIES", "0") == "1"
    if no_default_registries:
        registries = None
    else:
        registries = "docker://europe-southwest1-docker.pkg.dev/repositories-368911"
    executor = "local"

    # Stay backwards compatible
    app_root = os.environ.get("PM_APP_ROOT", None)
    if app_root is not None:
        # Normalize app_root - remove trailing slashes
        if app_root is not None:
            while app_root.endswith("/"):
                app_root = app_root[:-1]

        registries = [app_root]
        if app_root.startswith("http"):
            executor = app_root
        elif app_root.startswith("docker"):
            pass
        else:
            registries = [f"local:{app_root}"]
        logger.warning(
            f"PM_APP_ROOT is set to {app_root}. This is deprecated. Use PM_REGISTRIES={registries[0]} and PM_EXECUTOR={executor} instead."
        )

    # Get registries configuration
    registries = os.environ.get("PM_REGISTRIES", registries)
    registries = (
        None if isinstance(registries, str) and registries.strip() == "" else registries
    )
    if isinstance(registries, str):
        registries = None if registries.lower() == "none" else registries
        registries = registries.split(",") if registries is not None else []
        registries = (
            [registry.strip() for registry in registries]
            if registries is not None
            else []
        )

    # Remove trailing slashes from registries
    if registries is not None and len(registries) > 0:
        registries = [registry.rstrip("/") for registry in registries]

    # This is just for backwards compatibility with the old PM_SKIP_SETUP environment variable.
    skip_setup = os.environ.get("PM_SKIP_SETUP", "0") == "1"
    if skip_setup:
        logger.warning(
            "Deprecation warning: PM_SKIP_SETUP is deprecated. Use PM_REGISTRIES=none instead."
        )
        registries = None

    # Get executor configuration
    executor = os.environ.get("PM_EXECUTOR", executor)

    # Get runtime configuration
    runtime = os.environ.get("PM_RUNTIME", "docker")

    queue_config = None
    pm_queue_config = os.environ.get("PM_QUEUE_CONFIG", None)
    if pm_queue_config is not None:
        queue_config = json.loads(pm_queue_config)

    backend_headers = {}
    pm_backend_headers = os.environ.get("PM_BACKEND_HEADERS", None)
    if pm_backend_headers is not None:
        backend_headers = json.loads(pm_backend_headers)

    working_dir = None
    if "PM_WORKING_DIR" in os.environ:
        working_dir = os.path.abspath(os.environ["PM_WORKING_DIR"])

    sif_cache_dir = os.path.join(
        os.path.expanduser("~"), ".cache", "playmolecule", "apptainer"
    )
    if "PM_SIF_CACHE_DIR" in os.environ:
        sif_cache_dir = os.path.abspath(os.environ["PM_SIF_CACHE_DIR"])

    cookie_cache_dir = os.path.join(
        os.path.expanduser("~"), ".cache", "playmolecule", "cookies"
    )
    if "PM_COOKIE_CACHE_DIR" in os.environ:
        cookie_cache_dir = os.path.abspath(os.environ["PM_COOKIE_CACHE_DIR"])

    return _Config(
        registries=registries,
        executor=executor,
        runtime=runtime,
        queue_config=queue_config,
        symlink="PM_SYMLINK" in os.environ,
        blocking=os.environ.get("PM_BLOCKING", "0") == "1",
        backend_headers=backend_headers,
        job_dir_prefix=os.environ.get("PM_JOB_DIR_PREFIX", ""),
        working_dir=working_dir,
        sif_cache_dir=sif_cache_dir,
        cookie_cache_dir=cookie_cache_dir,
    )


# Global config instance - lazily initialized
_config: Optional[_Config] = None


def _get_config() -> _Config:
    """Get the global configuration instance.

    Returns
    -------
    _Config
        The global configuration object
    """
    global _config
    if _config is None:
        _config = _load_config()
    return _config


def _reset_config() -> None:
    """Reset the global configuration (useful for testing)."""
    global _config
    _config = None
