"""Replay test discovery and metadata parsing."""

from __future__ import annotations

import ast
import logging
from pathlib import Path

from .models import ReplayTestMetadata

log = logging.getLogger(__name__)


def is_replay_test(test_file: Path) -> bool:
    """Check whether *test_file* is a tracer-generated replay test."""
    return "__replay_test" in str(test_file)


def _extract_trace_path(node: ast.Assign) -> Path | None:
    """Extract trace_file_path from an assignment node."""
    for target in node.targets:
        if (
            isinstance(target, ast.Name)
            and target.id == "trace_file_path"
            and isinstance(node.value, ast.Constant)
            and isinstance(node.value.value, str)
        ):
            return Path(node.value.value)
    return None


def _extract_function_names(
    node: ast.Assign,
) -> tuple[str, ...] | None:
    """Extract function names from an assignment node."""
    for target in node.targets:
        if (
            isinstance(target, ast.Name)
            and target.id == "functions"
            and isinstance(node.value, ast.List)
        ):
            return tuple(
                elt.value
                for elt in node.value.elts
                if isinstance(elt, ast.Constant) and isinstance(elt.value, str)
            )
    return None


def parse_replay_test_metadata(
    test_file: Path,
) -> ReplayTestMetadata | None:
    """Parse metadata from a Python replay test file.

    Replay tests generated by the tracer contain module-level
    assignments:

    - ``functions = ["func1", "func2", ...]``
    - ``trace_file_path = r"/path/to/trace.db"``

    Returns *None* if the file cannot be parsed or lacks
    expected metadata.
    """
    try:
        with test_file.open("r", encoding="utf8") as f:
            tree = ast.parse(f.read())
    except (OSError, SyntaxError):
        log.warning(
            "Error parsing replay test file %s",
            test_file,
            exc_info=True,
        )
        return None

    trace_path: Path | None = None
    function_names: tuple[str, ...] | None = None

    for node in ast.walk(tree):
        if not isinstance(node, ast.Assign):
            continue
        if trace_path is None:
            trace_path = _extract_trace_path(node)
        if function_names is None:
            function_names = _extract_function_names(node)
        if trace_path is not None and function_names is not None:
            break

    if trace_path is None or function_names is None:
        return None

    return ReplayTestMetadata(
        trace_file_path=trace_path,
        function_names=function_names,
    )


def discover_replay_test_files(test_root: Path) -> list[Path]:
    """Find all replay test files under *test_root*.

    Looks for Python files with ``__replay_test`` in their path.
    """
    return sorted(p for p in test_root.rglob("*.py") if is_replay_test(p))
