"""Quality CLI commands - quality testing and reports."""
from __future__ import annotations

from pathlib import Path
from typing import Any

import click

from logos_adk.agent import Agent
from logos_adk.providers.mock import MockProvider
from logos_adk.quality_report import write_quality_report_file
from logos_adk.quality_suites import (
    load_schema,
    load_suite_file,
    parse_integration_suite,
    parse_judge_eval_suite,
    parse_load_test_suite,
    parse_regression_suite,
    render_suite_template,
)
from logos_adk.runnable_config_loader import load_runnable_from_config
from logos_adk.testing_framework import (
    GoldenResult,
    IntegrationResult,
    JudgeResult,
    LLMJudge,
    LoadTestReport,
    run_golden_dataset,
    run_integration_suite,
    run_llm_judge_eval,
    run_load_test,
)
from ..utils.animation import run_sync_coro

from ..utils.output import build_html_sections, write_reports


@click.group()
def quality() -> None:
    """Quality testing and reports."""


@quality.command()
@click.option(
    "--kind",
    type=click.Choice(["integration", "regression", "load-test", "judge-eval"]),
    required=True,
)
@click.option(
    "--config",
    "config_path",
    type=click.Path(exists=True, path_type=Path),
    help="Test suite config file",
)
@click.option("--output", "-o", type=click.Path(path_type=Path), help="Write report output directory")
@click.option("--agent-config", type=click.Path(exists=True, path_type=Path), help="Agent config for testing")
def test(kind: str, config_path: Path | None, output: Path | None, agent_config: Path | None) -> None:
    """Run quality tests."""
    click.echo(f"Running {kind} tests...")

    if kind == "integration":
        result = _run_integration_tests(config_path, agent_config)
    elif kind == "regression":
        result = _run_regression_tests(config_path, agent_config)
    elif kind == "load-test":
        result = _run_load_tests(config_path, agent_config)
    else:
        result = _run_judge_eval(config_path, agent_config)

    if output:
        title = f"{kind.replace('-', ' ').title()} Test Report"
        sections = build_html_sections(title, result.get("items", []))
        paths = write_reports(
            title=title,
            summary=result.get("summary", {}),
            sections=sections,
            output_dir=output,
        )
        click.echo("\nReports written:")
        for path in paths.values():
            click.echo(f"  - {path}")

    click.echo("\nTests completed")
    summary = result.get("summary", {})
    if "passed" in summary and "failed" in summary:
        total = int(summary["passed"]) + int(summary["failed"])
        click.echo(f"  Passed: {summary['passed']}/{total}")
        if int(summary["failed"]) > 0:
            raise SystemExit(1)


@quality.command()
@click.option("--kind", type=click.Choice(["integration", "regression", "load-test", "judge-eval"]), required=True)
def schema(kind: str) -> None:
    """Show test suite schema."""
    import json

    click.echo(json.dumps(load_schema(kind), indent=2))


@quality.command()
@click.option("--kind", type=click.Choice(["integration", "regression", "load-test", "judge-eval"]), required=True)
@click.option("--format", "output_format", type=click.Choice(["yaml", "json"]), default="yaml")
def template(kind: str, output_format: str) -> None:
    """Generate test suite template."""
    click.echo(render_suite_template(kind, format=output_format))


def _load_agent(agent_config: Path | None) -> Agent:
    if agent_config is None:
        return Agent("quality-bot", model=MockProvider(responses=["ok"]))
    runnable = load_runnable_from_config(str(agent_config))
    if not isinstance(runnable, Agent):
        raise click.ClickException("Quality commands currently support only Agent configs")
    return runnable


def _title_for_kind(kind: str) -> str:
    return f"{kind.replace('-', ' ').title()} Report"


def _write_selected_reports(
    *,
    kind: str,
    result: dict[str, Any],
    html_report: Path | None = None,
    markdown_report: Path | None = None,
    csv_report: Path | None = None,
) -> list[Path]:
    title = _title_for_kind(kind)
    sections = build_html_sections(title, result.get("items", []))
    written: list[Path] = []
    if html_report is not None:
        write_quality_report_file(
            html_report,
            title=title,
            summary=result.get("summary", {}),
            sections=sections,
            format="html",
        )
        written.append(html_report)
    if markdown_report is not None:
        write_quality_report_file(
            markdown_report,
            title=title,
            summary=result.get("summary", {}),
            sections=sections,
            format="md",
        )
        written.append(markdown_report)
    if csv_report is not None:
        write_quality_report_file(
            csv_report,
            title=title,
            summary=result.get("summary", {}),
            sections=sections,
            format="csv",
        )
        written.append(csv_report)
    return written


def _emit_pass_fail_summary(result: dict[str, Any]) -> int:
    summary = result.get("summary", {})
    passed = int(summary.get("passed", 0))
    failed = int(summary.get("failed", 0))
    total = int(summary.get("total", passed + failed))
    click.echo(f"{passed}/{total} passed")
    return failed


def _run_integration_tests(config_path: Path | None, agent_config: Path | None) -> dict[str, Any]:
    if config_path is None:
        scenarios = parse_integration_suite(
            {
                "scenarios": [
                    {
                        "name": "basic_chat",
                        "steps": [{"prompt": "Hello", "expect_contains": ["hello"]}],
                    }
                ]
            }
        )
    else:
        scenarios = parse_integration_suite(load_suite_file(str(config_path)))

    agent = _load_agent(agent_config)
    results = run_sync_coro(run_integration_suite(agent, scenarios))
    return _integration_summary(results)


def _run_regression_tests(config_path: Path | None, agent_config: Path | None) -> dict[str, Any]:
    if config_path is None:
        raise click.ClickException("--config is required for regression tests")
    examples = parse_regression_suite(load_suite_file(str(config_path)))
    agent = _load_agent(agent_config)
    results = run_sync_coro(run_golden_dataset(agent, examples))
    return _golden_summary(results)


def _run_load_tests(config_path: Path | None, agent_config: Path | None) -> dict[str, Any]:
    if config_path is None:
        raise click.ClickException("--config is required for load tests")
    config = parse_load_test_suite(load_suite_file(str(config_path)))
    agent = _load_agent(agent_config)
    result = run_sync_coro(run_load_test(agent, config))
    return _load_summary(result)


def _run_judge_eval(config_path: Path | None, agent_config: Path | None) -> dict[str, Any]:
    if config_path is None:
        raise click.ClickException("--config is required for judge eval")

    cases, judge_meta = parse_judge_eval_suite(load_suite_file(str(config_path)))
    response = str(judge_meta.get("mock_response", '{"score": 0.5, "rationale": "no judge configured"}'))
    judge = LLMJudge(_create_mock_judge_agent(response), pass_threshold=float(judge_meta.get("pass_threshold", 0.7)))
    agent = _load_agent(agent_config)
    results = run_sync_coro(run_llm_judge_eval(agent, cases, judge))
    return _judge_summary(results)


def _integration_summary(results: list[IntegrationResult]) -> dict[str, Any]:
    items = [
        {
            "name": item.name,
            "passed": item.passed,
            "error": item.error,
            "responses": [response.content for response in item.responses],
        }
        for item in results
    ]
    passed = sum(1 for item in results if item.passed)
    return {
        "summary": {"passed": passed, "failed": len(results) - passed, "total": len(results)},
        "items": items,
    }


def _golden_summary(results: list[GoldenResult]) -> dict[str, Any]:
    items = [
        {
            "name": item.name,
            "passed": item.passed,
            "actual": item.actual,
            "expected": item.expected,
            "mode": item.mode,
        }
        for item in results
    ]
    passed = sum(1 for item in results if item.passed)
    return {
        "summary": {"passed": passed, "failed": len(results) - passed, "total": len(results)},
        "items": items,
    }


def _load_summary(result: LoadTestReport) -> dict[str, Any]:
    payload = {
        "total_requests": result.total_requests,
        "success_count": result.success_count,
        "error_count": result.error_count,
        "avg_latency_ms": result.avg_latency_ms,
        "p95_latency_ms": result.p95_latency_ms,
        "max_latency_ms": result.max_latency_ms,
        "errors": result.errors,
    }
    return {"summary": payload, "items": [payload]}


def _judge_summary(results: list[JudgeResult]) -> dict[str, Any]:
    passed = sum(1 for item in results if item.passed)
    avg_score = sum(item.score for item in results) / len(results) if results else 0.0
    items = [
        {
            "name": item.name,
            "passed": item.passed,
            "score": item.score,
            "rationale": item.rationale,
            "answer": item.answer,
        }
        for item in results
    ]
    return {
        "summary": {"passed": passed, "failed": len(results) - passed, "total": len(results), "avg_score": avg_score},
        "items": items,
    }


def _create_mock_judge_agent(response: str) -> Agent:
    return Agent("judge", model=MockProvider(responses=[response]))


@click.command(name="integration")
@click.argument("agent_config", type=click.Path(exists=True, path_type=Path))
@click.argument("suite_config", type=click.Path(exists=True, path_type=Path))
@click.option("--html-report", type=click.Path(path_type=Path), help="Write HTML report")
@click.option("--markdown-report", type=click.Path(path_type=Path), help="Write Markdown report")
@click.option("--csv-report", type=click.Path(path_type=Path), help="Write CSV report")
def integration_command(
    agent_config: Path,
    suite_config: Path,
    html_report: Path | None,
    markdown_report: Path | None,
    csv_report: Path | None,
) -> None:
    """Run an integration suite from the CLI root."""
    result = _run_integration_tests(suite_config, agent_config)
    _write_selected_reports(
        kind="integration",
        result=result,
        html_report=html_report,
        markdown_report=markdown_report,
        csv_report=csv_report,
    )
    if _emit_pass_fail_summary(result) > 0:
        raise SystemExit(1)


@click.command(name="load-test")
@click.argument("agent_config", type=click.Path(exists=True, path_type=Path))
@click.argument("suite_config", type=click.Path(exists=True, path_type=Path))
@click.option("--output", type=click.Path(path_type=Path), help="Write JSON/YAML output")
def load_test_command(agent_config: Path, suite_config: Path, output: Path | None) -> None:
    """Run a load-test suite from the CLI root."""
    result = _run_load_tests(suite_config, agent_config)
    if output is not None:
        from ..utils.output import write_result_output

        write_result_output(output, result.get("summary", {}))
    summary = result.get("summary", {})
    click.echo(f"{int(summary.get('success_count', 0))}/{int(summary.get('total_requests', 0))} successful")
    if int(summary.get("error_count", 0)) > 0:
        raise SystemExit(1)


@click.command(name="regression")
@click.argument("agent_config", type=click.Path(exists=True, path_type=Path))
@click.argument("suite_config", type=click.Path(exists=True, path_type=Path))
def regression_command(agent_config: Path, suite_config: Path) -> None:
    """Run a regression suite from the CLI root."""
    result = _run_regression_tests(suite_config, agent_config)
    if _emit_pass_fail_summary(result) > 0:
        raise SystemExit(1)


@click.command(name="judge-eval")
@click.argument("agent_config", type=click.Path(exists=True, path_type=Path))
@click.argument("suite_config", type=click.Path(exists=True, path_type=Path))
def judge_eval_command(agent_config: Path, suite_config: Path) -> None:
    """Run a judge-eval suite from the CLI root."""
    result = _run_judge_eval(suite_config, agent_config)
    if _emit_pass_fail_summary(result) > 0:
        raise SystemExit(1)


__all__ = [
    "quality",
    "test",
    "schema",
    "template",
    "integration_command",
    "load_test_command",
    "regression_command",
    "judge_eval_command",
]
