"""Tests for the AgentModule."""

from __future__ import annotations

from unittest.mock import AsyncMock, patch

import pytest

from openhedge.exceptions import AuthenticationError
from openhedge.models.agent import AgentProfile, AgentRegistration
from tests.conftest import FAKE_API_KEY, FAKE_TX_HASH, FAKE_VAULT, make_api_response, make_rpc_response


class TestAgentRegister:
    async def test_register_success(self, agent):
        response_data = {
            "id": "agent_abc",
            "wallet_address": "0x" + "aa" * 20,
            "name": "TestBot",
            "approved": False,
        }
        with patch.object(
            agent._http, "post", new_callable=AsyncMock, return_value=make_api_response(response_data)
        ):
            reg = AgentRegistration(
                wallet_address="0x" + "aa" * 20,
                name="TestBot",
            )
            profile = await agent.agent.register(reg)
            assert isinstance(profile, AgentProfile)
            assert profile.id == "agent_abc"
            assert profile.name == "TestBot"

    async def test_register_auth_error(self, agent):
        with patch.object(
            agent._http, "post", new_callable=AsyncMock, return_value=make_api_response({}, 401)
        ):
            reg = AgentRegistration(
                wallet_address="0x" + "aa" * 20,
                name="TestBot",
            )
            with pytest.raises(AuthenticationError):
                await agent.agent.register(reg)


class TestAgentStatus:
    async def test_status_success(self, agent):
        response_data = {
            "id": "agent_abc",
            "wallet_address": "0x" + "aa" * 20,
            "name": "TestBot",
            "approved": True,
            "vault_address": "0x" + "bb" * 20,
        }
        with patch.object(
            agent._http, "get", new_callable=AsyncMock, return_value=make_api_response(response_data)
        ):
            profile = await agent.agent.status()
            assert profile.approved is True
            assert profile.vault_address is not None


class TestAgentIssueSessionKey:
    async def test_issue_session_key_success(self, agent):
        fake_key_id = "0x" + "cc" * 32
        mock_receipt = {
            "transactionHash": FAKE_TX_HASH,
            "blockNumber": "0x10",
            "gasUsed": "0x30d40",
            "status": "0x1",
            "logs": [
                {
                    "topics": [
                        "0x" + "ab" * 32,  # event sig
                        "0x" + "00" * 12 + "11" * 20,  # vault
                        "0x" + "00" * 12 + "22" * 20,  # sessionKey
                        fake_key_id,  # keyId
                    ]
                }
            ],
        }

        responses = [
            make_rpc_response("0x5"),           # nonce
            make_rpc_response("0x493e0"),        # gas estimate
            make_rpc_response("0x3b9aca00"),     # gas price
            make_rpc_response(FAKE_TX_HASH),     # sendRawTransaction
            make_rpc_response(mock_receipt),      # receipt
        ]
        call_count = 0

        async def mock_post(*args, **kwargs):
            nonlocal call_count
            resp = responses[min(call_count, len(responses) - 1)]
            call_count += 1
            return resp

        with patch.object(agent._http, "post", side_effect=mock_post):
            result = await agent.agent.issue_session_key(
                vault=FAKE_VAULT,
                session_key="0x" + "22" * 20,
                duration_seconds=3600,
                max_trade_size=1_000_000,
            )
            assert result["tx_hash"] == FAKE_TX_HASH
            assert result["key_id"] == fake_key_id


class TestAgentRevokeSessionKey:
    async def test_revoke_session_key_success(self, agent):
        mock_receipt = {
            "transactionHash": FAKE_TX_HASH,
            "blockNumber": "0x10",
            "gasUsed": "0x30d40",
            "status": "0x1",
            "logs": [],
        }

        responses = [
            make_rpc_response("0x5"),
            make_rpc_response("0x493e0"),
            make_rpc_response("0x3b9aca00"),
            make_rpc_response(FAKE_TX_HASH),
            make_rpc_response(mock_receipt),
        ]
        call_count = 0

        async def mock_post(*args, **kwargs):
            nonlocal call_count
            resp = responses[min(call_count, len(responses) - 1)]
            call_count += 1
            return resp

        with patch.object(agent._http, "post", side_effect=mock_post):
            tx_hash = await agent.agent.revoke_session_key(key_id="0x" + "cc" * 32)
            assert tx_hash == FAKE_TX_HASH
