"""Tests for MeshLedger CrewAI integration."""

import pytest
import httpx
import respx

# Skip entire module if crewai is not installed
crewai = pytest.importorskip("crewai")

from meshledger import MeshLedgerClient
from meshledger.integrations.crewai import (
    MeshLedgerCrewTools,
    BrowseSkillsCrewTool,
    SearchAgentsCrewTool,
    GetSkillDetailsCrewTool,
    CreateJobCrewTool,
    AcceptJobCrewTool,
    DeliverJobCrewTool,
    CheckJobCrewTool,
    ReleasePaymentCrewTool,
    DisputeJobCrewTool,
    RateJobCrewTool,
    GetDashboardCrewTool,
    MarketplaceStatsCrewTool,
)

BASE = "https://meshledger.io/api/v1"


def ok(data):
    return httpx.Response(200, json={"success": True, "data": data})


def err(status, error="fail", code="ERROR"):
    return httpx.Response(status, json={"success": False, "error": error, "code": code})


# ---------------------------------------------------------------------------
# Toolkit
# ---------------------------------------------------------------------------


class TestMeshLedgerCrewTools:
    def test_creates_all_12_tools(self):
        toolkit = MeshLedgerCrewTools(api_key="ml_sk_test")
        tools = toolkit.get_tools()
        assert len(tools) == 12

    def test_tool_names(self):
        toolkit = MeshLedgerCrewTools(api_key="ml_sk_test")
        tools = toolkit.get_tools()
        names = {t.name for t in tools}
        expected = {
            "meshledger_browse_skills",
            "meshledger_search_agents",
            "meshledger_get_skill_details",
            "meshledger_create_job",
            "meshledger_accept_job",
            "meshledger_deliver_job",
            "meshledger_check_job",
            "meshledger_release_payment",
            "meshledger_dispute_job",
            "meshledger_rate_job",
            "meshledger_dashboard",
            "meshledger_marketplace_stats",
        }
        assert names == expected

    def test_all_tools_have_descriptions(self):
        toolkit = MeshLedgerCrewTools(api_key="ml_sk_test")
        for tool in toolkit.get_tools():
            assert tool.description, f"{tool.name} missing description"
            assert len(tool.description) > 10, f"{tool.name} description too short"

    def test_all_tools_have_args_schema(self):
        toolkit = MeshLedgerCrewTools(api_key="ml_sk_test")
        for tool in toolkit.get_tools():
            assert tool.args_schema is not None, f"{tool.name} missing args_schema"

    def test_custom_base_url(self):
        toolkit = MeshLedgerCrewTools(
            api_key="ml_sk_test", base_url="https://custom.example.com/api/v1"
        )
        assert toolkit.client.base_url == "https://custom.example.com/api/v1"


# ---------------------------------------------------------------------------
# Individual tools
# ---------------------------------------------------------------------------


class TestBrowseSkillsCrewTool:
    @respx.mock
    def test_returns_skills(self):
        respx.get(f"{BASE}/skills").mock(
            return_value=ok(
                {
                    "skills": [
                        {
                            "skill_id": "s1",
                            "agent_id": "a1",
                            "name": "Code Review",
                            "description": "Reviews code for bugs",
                            "capabilities": ["code-review"],
                            "price": 5.0,
                            "price_token": "USDC",
                            "price_chain": "base",
                        }
                    ],
                    "cursor": None,
                    "has_more": False,
                }
            )
        )
        client = MeshLedgerClient()
        tool = BrowseSkillsCrewTool(client=client)
        result = tool._run()
        assert "Code Review" in result
        assert "s1" in result
        client.close()

    @respx.mock
    def test_no_skills_found(self):
        respx.get(f"{BASE}/skills").mock(
            return_value=ok({"skills": [], "cursor": None, "has_more": False})
        )
        client = MeshLedgerClient()
        tool = BrowseSkillsCrewTool(client=client)
        result = tool._run()
        assert "No skills found" in result
        client.close()

    @respx.mock
    def test_error_returns_message(self):
        respx.get(f"{BASE}/skills").mock(return_value=err(500, "Server error"))
        client = MeshLedgerClient()
        tool = BrowseSkillsCrewTool(client=client)
        result = tool._run()
        assert "Error" in result
        client.close()


class TestSearchAgentsCrewTool:
    @respx.mock
    def test_returns_agents(self):
        respx.get(f"{BASE}/agents/search").mock(
            return_value=ok(
                {
                    "agents": [
                        {
                            "agent_id": "a1",
                            "name": "TestBot",
                            "description": "A test bot",
                            "chains": ["base"],
                            "capabilities": ["testing"],
                        }
                    ],
                    "cursor": None,
                    "has_more": False,
                }
            )
        )
        client = MeshLedgerClient()
        tool = SearchAgentsCrewTool(client=client)
        result = tool._run(capabilities="testing")
        assert "TestBot" in result
        client.close()


class TestCreateJobCrewTool:
    @respx.mock
    def test_creates_job(self):
        respx.post(f"{BASE}/jobs").mock(
            return_value=httpx.Response(
                201,
                json={
                    "success": True,
                    "data": {
                        "job_id": "j1",
                        "escrow_tx_hash": "0xabc",
                        "total_charged": 10.5,
                        "listed_price": 10.0,
                        "service_fee": 0.5,
                        "status": "funded",
                    },
                },
            )
        )
        client = MeshLedgerClient(api_key="ml_sk_test")
        tool = CreateJobCrewTool(client=client)
        result = tool._run(
            title="Test", description="Do it", chain="base", token="USDC", price=10.0
        )
        assert "j1" in result
        assert "10.5" in result
        client.close()


class TestCheckJobCrewTool:
    @respx.mock
    def test_checks_job(self):
        respx.get(f"{BASE}/jobs/j1").mock(
            return_value=ok(
                {
                    "job_id": "j1",
                    "title": "Test Job",
                    "status": "accepted",
                    "listed_price": 10.0,
                    "token": "USDC",
                    "chain": "base",
                }
            )
        )
        client = MeshLedgerClient()
        tool = CheckJobCrewTool(client=client)
        result = tool._run(job_id="j1")
        assert "Test Job" in result
        assert "accepted" in result
        client.close()


class TestMarketplaceStatsCrewTool:
    @respx.mock
    def test_returns_stats(self):
        respx.get(f"{BASE}/marketplace/stats").mock(
            return_value=ok(
                {
                    "total_agents": 100,
                    "total_skills": 500,
                    "total_jobs_completed": 1000,
                    "total_volume_usd": 50000.0,
                    "active_escrows": 25,
                }
            )
        )
        client = MeshLedgerClient()
        tool = MarketplaceStatsCrewTool(client=client)
        result = tool._run()
        assert "100" in result
        assert "500" in result
        assert "50,000.00" in result
        client.close()
