from __future__ import annotations

import asyncio
import json
from types import SimpleNamespace

import httpx

from autodebug.agent.core import HikaflowDeps
from autodebug.agent.tools import register_hikaflow_tools


class _DummyAgent:
    def __init__(self):
        self.tools = {}

    def tool(self, fn):
        self.tools[fn.__name__] = fn
        return fn


def _ctx(tmp_path):
    deps = HikaflowDeps(
        api_base="https://api.example.com",
        token="tkn",
        project_path=str(tmp_path),
    )
    return SimpleNamespace(deps=deps)


def _tools():
    agent = _DummyAgent()
    register_hikaflow_tools(agent)
    return agent.tools


def _assert_envelope(raw: str):
    parsed = json.loads(raw)
    assert parsed["status"] in {"ok", "partial", "error"}
    assert isinstance(parsed["summary"], str)
    assert len(parsed["summary"]) <= 240
    assert isinstance(parsed["data"], dict)
    assert isinstance(parsed["confidence"], (int, float))
    return parsed


def _arun(coro):
    return asyncio.run(coro)


def test_incident_overview_rejects_invalid_environment(tmp_path):
    result = _arun(_tools()["incident_overview"](_ctx(tmp_path), environment="dev"))
    payload = _assert_envelope(result)
    assert payload["status"] == "error"
    assert isinstance(payload["data"]["related_failures"], list)
    assert "valid environment" in payload["next_actions"][0].lower()


def test_failure_similarity_search_requires_identity(tmp_path):
    result = _arun(_tools()["failure_similarity_search"](_ctx(tmp_path)))
    payload = _assert_envelope(result)
    assert payload["status"] == "error"
    assert payload["data"]["matches"] == []


def test_generate_fix_candidates_requires_error_or_run_id(tmp_path):
    result = _arun(_tools()["generate_fix_candidates"](_ctx(tmp_path)))
    payload = _assert_envelope(result)
    assert payload["status"] == "error"
    assert payload["data"]["candidates"] == []
    assert isinstance(payload["data"]["no_fix_reason"], str)


def test_create_fix_pr_requires_confirmation_and_id(tmp_path):
    result_missing = _arun(_tools()["create_fix_pr"](_ctx(tmp_path)))
    missing = _assert_envelope(result_missing)
    assert missing["status"] == "error"

    result_confirm = _arun(_tools()["create_fix_pr"](_ctx(tmp_path), run_id="run-1", confirm=False))
    confirm = _assert_envelope(result_confirm)
    assert confirm["status"] == "error"
    assert confirm["data"]["requires_confirmation"] is True


def test_quarantine_control_action_matrix_validation(tmp_path):
    result = _arun(_tools()["quarantine_control"](
        _ctx(tmp_path),
        scope="local",
        action="add",
        test_id=None,
    ))
    payload = _assert_envelope(result)
    assert payload["status"] == "error"
    assert payload["data"]["action"] == "add"
    assert payload["data"]["tests"] == []


def test_codebase_scan_scope_validation(tmp_path):
    result = _arun(_tools()["codebase_scan"](_ctx(tmp_path), scope="weird"))
    payload = _assert_envelope(result)
    assert payload["status"] == "error"
    assert payload["data"]["scan_meta"]["scope"] == "weird"


def test_knowledge_memory_rejects_unknown_action(tmp_path):
    result = _arun(_tools()["knowledge_memory"](_ctx(tmp_path), action="bogus", payload={}))
    payload = _assert_envelope(result)
    assert payload["status"] == "error"
    assert "counts" in payload["data"]


def test_project_health_snapshot_detail_level_validation(tmp_path):
    result = _arun(_tools()["project_health_snapshot"](_ctx(tmp_path), detail_level="full"))
    payload = _assert_envelope(result)
    assert payload["status"] == "error"
    assert "project_health" in payload["data"]


def test_create_fix_pr_requires_repo_fields(tmp_path):
    result = _arun(
        _tools()["create_fix_pr"](
            _ctx(tmp_path),
            run_id="run-1",
            confirm=True,
            repo_owner="",
            repo_name="",
        )
    )
    payload = _assert_envelope(result)
    assert payload["status"] == "error"
    assert "repo_owner" in payload["summary"]


def test_create_fix_pr_server_path_success(monkeypatch, tmp_path):
    class _FakeAsyncClient:
        async def __aenter__(self):
            return self

        async def __aexit__(self, exc_type, exc, tb):
            return False

        async def request(self, method, url, headers=None, params=None, json=None, timeout=None):
            req = httpx.Request(method, url)
            return httpx.Response(
                200,
                request=req,
                json={
                    "pr_url": "https://github.com/o/r/pull/123",
                    "pr_number": 123,
                    "branch": "fix-123",
                },
            )

    monkeypatch.setattr("autodebug.agent.tools.httpx.AsyncClient", lambda: _FakeAsyncClient())
    result = _arun(
        _tools()["create_fix_pr"](
            _ctx(tmp_path),
            fix_id="fix-123",
            repo_owner="o",
            repo_name="r",
            base_branch="main",
            confirm=True,
        )
    )
    payload = _assert_envelope(result)
    assert payload["status"] == "ok"
    assert payload["data"]["pr_url"].endswith("/pull/123")
    assert payload["data"]["pr_number"] == 123
