"""
A decorator takes a callable A and returns a callable B that will override the name of A.
A decorator with arguments returns a closure decorator having access to the arguments.

Example usage for the location decorators:

class MyClass():
    @cached_property
    @replaced_by_schema_key(key="source-location")
    @replaced_by_path(override="/somewhere/that/doesnt/exist")
    @replaced_by_path(override="/somewhere/that/exists")
    def location(self):
        return None
"""

from pathlib import Path
from os import PathLike, environ
from functools import wraps

from src import shared


def replaced_by_path(override: PathLike):  # Decorator builder
    """Replace the method's returned path with the override
    if the override exists on disk"""

    def decorator(original_function):  # Built decorator (closure)
        @wraps(original_function)
        def wrapper(*args, **kwargs):  # func's override
            path = Path(override).expanduser()
            if path.exists():
                return path
            return original_function(*args, **kwargs)

        return wrapper

    return decorator


def replaced_by_schema_key(key: str):  # Decorator builder
    """Replace the method's returned path with the path pointed by the key
    if it exists on disk"""

    def decorator(original_function):  # Built decorator (closure)
        @wraps(original_function)
        def wrapper(*args, **kwargs):  # func's override
            try:
                override = shared.schema.get_string(key)
            except Exception:  # pylint: disable=broad-exception-caught
                return original_function(*args, **kwargs)
            return replaced_by_path(override)(original_function)(*args, **kwargs)

        return wrapper

    return decorator


def replaced_by_env_path(env_var_name: str, suffix: PathLike | None = None):
    """Replace the method's returned path with a path whose root is the env variable"""

    def decorator(original_function):  # Built decorator (closure)
        @wraps(original_function)
        def wrapper(*args, **kwargs):  # func's override
            try:
                env_var = environ[env_var_name]
            except KeyError:
                return original_function(*args, **kwargs)
            override = Path(env_var) / suffix
            return replaced_by_path(override)(original_function)(*args, **kwargs)

        return wrapper

    return decorator
