from __future__ import annotations

import logging
import os
import re
from pathlib import Path

import yaml

from leshy.core.models import TestCaseConfig, TestSuiteConfig

logger = logging.getLogger(__name__)

_ENV_PATTERN = re.compile(r"\$\{(\w+)\}")


def _interpolate_env(text: str) -> str:
    """Replace ${VAR_NAME} patterns with environment variable values."""

    def replacer(match: re.Match[str]) -> str:
        var_name = match.group(1)
        value = os.environ.get(var_name)
        if value is None:
            raise ValueError(
                f"Environment variable '{var_name}' is not set "
                f"(referenced as ${{{var_name}}} in config)"
            )
        return value

    return _ENV_PATTERN.sub(replacer, text)


def _load_tests_from_directory(tests_dir: Path) -> list[TestCaseConfig]:
    """Load test cases from all YAML files in a directory tree."""
    if not tests_dir.is_dir():
        raise ValueError(f"Tests directory not found: {tests_dir}")

    all_tests: list[TestCaseConfig] = []
    for yaml_file in sorted(tests_dir.rglob("*.yaml")):
        raw_text = yaml_file.read_text()
        interpolated = _interpolate_env(raw_text)
        data = yaml.safe_load(interpolated)
        if not isinstance(data, dict) or "tests" not in data:
            logger.debug("Skipping %s (no 'tests' key)", yaml_file)
            continue
        for test_data in data["tests"]:
            all_tests.append(TestCaseConfig(**test_data))

    return all_tests


def load_config(path: str | Path) -> TestSuiteConfig:
    """Load and validate a YAML config file into a TestSuiteConfig."""
    path = Path(path)
    if not path.exists():
        raise FileNotFoundError(f"Config file not found: {path}")

    raw_text = path.read_text()
    interpolated = _interpolate_env(raw_text)
    data = yaml.safe_load(interpolated)

    if not isinstance(data, dict):
        raise ValueError(f"Config file must contain a YAML mapping, got {type(data).__name__}")

    config = TestSuiteConfig(**data)

    if config.tests_dir:
        dir_path = (path.parent / config.tests_dir).resolve()
        extra_tests = _load_tests_from_directory(dir_path)
        config.tests.extend(extra_tests)

    return config
