"""
Variable extraction and resolution for ResolutionFlow tree structures.

Supports three variable tokens:
- [USER_INPUT:prompt] — prompts user for input during session navigation
- [VAR:name] — references a previously saved variable value
- [SAVE_AS:name] — saves the current context as a named variable
"""
import re
from dataclasses import dataclass
from typing import Any


@dataclass
class VariableDefinition:
    """A variable found in a tree structure."""
    name: str
    kind: str  # 'user_input', 'reference', 'save_as'
    prompt: str  # For user_input: the prompt text; for others: empty
    node_id: str  # The node where this variable appears


# Regex patterns for variable tokens
USER_INPUT_RE = re.compile(r'\[USER_INPUT:([^\]]+)\]')
VAR_REF_RE = re.compile(r'\[VAR:([^\]]+)\]')
SAVE_AS_RE = re.compile(r'\[SAVE_AS:([^\]]+)\]')


def extract_variables(tree_structure: dict[str, Any]) -> list[VariableDefinition]:
    """Extract all variable definitions from a tree structure.

    Traverses the tree recursively and finds all [USER_INPUT:...],
    [VAR:...], and [SAVE_AS:...] tokens.

    Args:
        tree_structure: The recursive tree_structure dict.

    Returns:
        List of VariableDefinition objects found in the tree.
    """
    variables: list[VariableDefinition] = []
    _scan_node(tree_structure, variables)
    return variables


def _scan_node(node: dict[str, Any], variables: list[VariableDefinition]) -> None:
    """Scan a node and its children for variable tokens."""
    node_id = node.get("id", "unknown")

    # Collect all text fields to scan
    text_fields = [
        node.get("question", ""),
        node.get("title", ""),
        node.get("description", ""),
        node.get("help_text", ""),
        node.get("expected_outcome", ""),
        node.get("solution", ""),
    ]

    # Include option labels
    for opt in node.get("options", []):
        text_fields.append(opt.get("label", ""))

    # Include resolution steps
    for step in node.get("resolution_steps", []):
        text_fields.append(step)

    # Include commands
    for cmd in node.get("commands", []):
        text_fields.append(cmd)

    # Scan all text fields
    for text in text_fields:
        if not text:
            continue

        for match in USER_INPUT_RE.finditer(text):
            variables.append(VariableDefinition(
                name=match.group(1).strip(),
                kind="user_input",
                prompt=match.group(1).strip(),
                node_id=node_id,
            ))

        for match in VAR_REF_RE.finditer(text):
            variables.append(VariableDefinition(
                name=match.group(1).strip(),
                kind="reference",
                prompt="",
                node_id=node_id,
            ))

        for match in SAVE_AS_RE.finditer(text):
            variables.append(VariableDefinition(
                name=match.group(1).strip(),
                kind="save_as",
                prompt="",
                node_id=node_id,
            ))

    # Recurse into children
    for child in node.get("children", []):
        _scan_node(child, variables)


def resolve_variables(text: str, variables: dict[str, str]) -> str:
    """Replace variable tokens in text with their resolved values.

    - [VAR:name] → replaced with variables[name] if present
    - [USER_INPUT:prompt] → replaced with variables[prompt] if present
    - [SAVE_AS:name] → removed (save directives don't appear in output)

    Args:
        text: The text containing variable tokens.
        variables: Dict mapping variable names to their values.

    Returns:
        Text with variable tokens replaced.
    """
    def _replace_var(match: re.Match) -> str:
        name = match.group(1).strip()
        return variables.get(name, f"[VAR:{name}]")

    def _replace_input(match: re.Match) -> str:
        prompt = match.group(1).strip()
        return variables.get(prompt, f"[USER_INPUT:{prompt}]")

    def _replace_save(match: re.Match) -> str:
        return ""  # Save directives are removed from output

    result = VAR_REF_RE.sub(_replace_var, text)
    result = USER_INPUT_RE.sub(_replace_input, result)
    result = SAVE_AS_RE.sub(_replace_save, result)
    return result
