"""YAML-based test runner for Logos ADK agents."""
from __future__ import annotations

import asyncio
import os
from dataclasses import dataclass
from typing import Any

import yaml

from logos_adk.agent import Agent
from logos_adk.errors import GuardrailError
from logos_adk.guardrails import ContentFilter
from logos_adk.providers.mock import MockProvider
from logos_adk.testing import assert_response


@dataclass
class TestResult:
    """Result of a single test case."""

    __test__ = False

    name: str
    passed: bool
    error: str | None = None


@dataclass
class TestResults:
    """Aggregated test results."""

    __test__ = False

    results: list[TestResult]
    passed: int
    failed: int
    total: int


def _build_agent(data: dict[str, Any], base_dir: str) -> Agent:
    """Build agent from YAML test suite config."""
    agent_path = data.get("agent")
    mock_responses = data.get("mock_responses", ["mock response"])

    if agent_path:
        resolved = os.path.join(base_dir, agent_path) if not os.path.isabs(agent_path) else agent_path
        agent = Agent.from_config(resolved)
    else:
        agent = Agent("test_agent")

    provider = MockProvider(responses=mock_responses)
    agent._config.model = provider

    # Apply guardrails from YAML
    guardrails = data.get("guardrails", [])
    for g in guardrails:
        if "blocked" in g:
            agent.input_guardrail(ContentFilter(blocked=g["blocked"]))

    return agent


async def _run_single_test(agent: Agent, test_case: dict[str, Any]) -> TestResult:
    """Run a single test case against an agent."""
    name = test_case.get("name", "unnamed")
    prompt = test_case["input"]
    expect = test_case.get("expect", {})

    try:
        if expect.get("raises") == "GuardrailError":
            try:
                await agent.run(prompt)
                return TestResult(name=name, passed=False, error="GuardrailError was not raised")
            except GuardrailError:
                return TestResult(name=name, passed=True)

        response = await agent.run(prompt)
        checks = {k: v for k, v in expect.items() if k != "raises"}
        if checks:
            assert_response(response, **checks)

        return TestResult(name=name, passed=True)

    except (AssertionError, Exception) as exc:
        return TestResult(name=name, passed=False, error=str(exc))


def run_yaml_tests(path: str) -> TestResults:
    """Run a YAML test suite file.

    Args:
        path: Path to a YAML test suite file.

    Returns:
        TestResults with individual test outcomes.
    """
    with open(path) as f:
        data = yaml.safe_load(f)

    base_dir = os.path.dirname(os.path.abspath(path))
    tests = data.get("tests", [])

    async def _run_all() -> list[TestResult]:
        res: list[TestResult] = []
        for test_case in tests:
            agent = _build_agent(data, base_dir)
            r = await _run_single_test(agent, test_case)
            res.append(r)
        return res

    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = None

    if loop and loop.is_running():
        import concurrent.futures
        with concurrent.futures.ThreadPoolExecutor() as pool:
            results = pool.submit(lambda: asyncio.run(_run_all())).result()
    else:
        results = asyncio.run(_run_all())

    passed = sum(1 for r in results if r.passed)
    failed = sum(1 for r in results if not r.passed)

    return TestResults(results=results, passed=passed, failed=failed, total=len(results))
