"""Deal with YAML input."""
from __future__ import annotations

from typing import Any

from .objects import Input


class UndefinedSubstitution(Exception):
    """Error raised when we find a substitution that is not defined."""

    def __init__(self, input_name: str) -> None:
        """Initialize the undefined substitution exception."""
        super().__init__(f"No substitution found for input {input_name}")
        self.input = input


def extract_inputs(obj: Any) -> set[str]:
    """Extract input from a structure."""
    found: set[str] = set()
    _extract_inputs(obj, found)
    return found


def _extract_inputs(obj: Any, found: set[str]) -> None:
    """Extract input from a structure."""
    if isinstance(obj, Input):
        found.add(obj.name)
        return

    if isinstance(obj, list):
        for val in obj:
            _extract_inputs(val, found)
        return

    if isinstance(obj, dict):
        for val in obj.values():
            _extract_inputs(val, found)
        return


def substitute(obj: Any, substitutions: dict[str, Any]) -> Any:
    """Substitute values."""
    if isinstance(obj, Input):
        if obj.name not in substitutions:
            raise UndefinedSubstitution(obj.name)
        return substitutions[obj.name]

    if isinstance(obj, list):
        return [substitute(val, substitutions) for val in obj]

    if isinstance(obj, dict):
        return {key: substitute(val, substitutions) for key, val in obj.items()}

    return obj
