from __future__ import annotations

from typing import Any

from acl_engine.context import ACLVariableError, ContextStore
from acl_engine.parser import VariableRef


def resolve_params(params: dict[str, Any], context: ContextStore) -> dict[str, Any]:
    return {name: _resolve_value(value, context) for name, value in params.items()}


def _resolve_value(value: Any, context: ContextStore) -> Any:
    if isinstance(value, VariableRef):
        return _resolve_variable_ref(value, context)
    if isinstance(value, list):
        return [_resolve_value(item, context) for item in value]
    if isinstance(value, dict):
        return {key: _resolve_value(item, context) for key, item in value.items()}
    return value


def _resolve_variable_ref(reference: VariableRef, context: ContextStore) -> Any:
    current = context.get(reference.name)

    for selector in reference.selectors:
        try:
            if isinstance(selector, int):
                current = current[selector]
            elif isinstance(current, dict):
                current = current[selector]
            elif hasattr(current, selector):
                current = getattr(current, selector)
            else:
                current = current[selector]
        except (KeyError, IndexError, TypeError, AttributeError) as error:
            raise _context_error(reference) from error

    return current


def _context_error(reference: VariableRef) -> ACLVariableError:
    return ACLVariableError(
        reference.name,
        f"unable to resolve variable path: {_render_reference(reference)}",
    )


def _render_reference(reference: VariableRef) -> str:
    parts = [reference.name]
    for selector in reference.selectors:
        if isinstance(selector, int):
            parts.append(f"[{selector}]")
        else:
            parts.append(f".{selector}")
    return "".join(parts)