"""Tests for Botcoin client."""
import json
import tempfile
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from botcoin import AuthError, Botcoin, BotcoinError, NotFoundError
from botcoin.auth import BotcoinAuth


class TestBotcoinClient:
    """Tests for Botcoin client class."""

    def test_from_credentials_creates_client(self):
        """Test creating client with explicit credentials."""
        private_hex, public_hex = BotcoinAuth.generate()

        bot = Botcoin.from_credentials(
            agent_id="test-123",
            private_key=private_hex,
            public_key=public_hex,
        )

        assert bot.agent_id == "test-123"

    def test_load_from_file(self):
        """Test loading client from credentials file."""
        private_hex, public_hex = BotcoinAuth.generate()

        with tempfile.TemporaryDirectory() as tmpdir:
            path = Path(tmpdir) / "agent.json"
            with open(path, "w") as f:
                json.dump(
                    {
                        "agent_id": "loaded-agent",
                        "private_key": private_hex,
                        "public_key": public_hex,
                    },
                    f,
                )

            bot = Botcoin.load(path=path)

            assert bot.agent_id == "loaded-agent"

    @patch("botcoin.client.requests.post")
    def test_new_agent_registration(self, mock_post):
        """Test new agent registration flow."""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"id": "new-agent-id", "name": "test-bot"}
        mock_post.return_value = mock_response

        with tempfile.TemporaryDirectory() as tmpdir:
            # Temporarily redirect credentials path
            with patch("botcoin.client.CREDENTIALS_PATH", Path(tmpdir) / "agent.json"):
                bot = Botcoin("test-bot", domains=["coding"], save_credentials=False)

        assert bot.agent_id == "new-agent-id"
        # Verify registration was called
        mock_post.assert_called_once()
        call_args = mock_post.call_args
        assert "/api/v1/agents" in call_args[0][0]

    @patch("botcoin.client.requests.post")
    def test_registration_failure_raises_error(self, mock_post):
        """Test that registration failure raises BotcoinError."""
        mock_response = MagicMock()
        mock_response.status_code = 400
        mock_response.text = "Bad request"
        mock_post.return_value = mock_response

        with pytest.raises(BotcoinError, match="Registration failed"):
            Botcoin("failing-bot", save_credentials=False)

    @patch("botcoin.client.requests.post")
    def test_remember_calls_correct_endpoint(self, mock_post):
        """Test that remember() calls the store endpoint."""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "memory": {
                "id": "mem-123",
                "content": "Test memory",
                "tags": ["test"],
                "memory_type": "fact",
            }
        }
        mock_post.return_value = mock_response

        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("test-agent", private_hex, public_hex)

        memory = bot.remember("Test memory", tags=["test"])

        assert memory.id == "mem-123"
        assert memory.content == "Test memory"

        # Verify endpoint
        call_url = mock_post.call_args[0][0]
        assert "/api/v1/memory/store" in call_url

    @patch("botcoin.client.requests.post")
    def test_search_returns_results(self, mock_post):
        """Test that search() returns SearchResult objects."""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "memories": [
                {"id": "m1", "content": "Result 1", "relevance": 0.9, "tags": []},
                {"id": "m2", "content": "Result 2", "relevance": 0.8, "tags": []},
            ]
        }
        mock_post.return_value = mock_response

        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("test-agent", private_hex, public_hex)

        results = bot.search("test query")

        assert len(results) == 2
        assert results[0].content == "Result 1"
        assert results[0].relevance == 0.9

    @patch("botcoin.client.requests.post")
    def test_marketplace_returns_listings(self, mock_post):
        """Test that marketplace() returns Listing objects."""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "signals": [
                {"id": "sig-1", "title": "Knowledge 1", "price": 5.0, "seller_name": "seller1"},
            ]
        }
        mock_post.return_value = mock_response

        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("test-agent", private_hex, public_hex)

        listings = bot.marketplace("test query")

        assert len(listings) == 1
        assert listings[0].title == "Knowledge 1"
        assert listings[0].price == 5.0

    @patch("botcoin.client.requests.post")
    def test_auth_error_on_401(self, mock_post):
        """Test that 401 raises AuthError."""
        mock_response = MagicMock()
        mock_response.status_code = 401
        mock_post.return_value = mock_response

        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("test-agent", private_hex, public_hex)

        with pytest.raises(AuthError):
            bot.remember("test")

    @patch("botcoin.client.requests.post")
    def test_not_found_error_on_404(self, mock_post):
        """Test that 404 raises NotFoundError."""
        mock_response = MagicMock()
        mock_response.status_code = 404
        mock_post.return_value = mock_response

        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("test-agent", private_hex, public_hex)

        with pytest.raises(NotFoundError):
            bot.remember("test")

    @patch("botcoin.client.requests.get")
    def test_stats_returns_network_stats(self, mock_get):
        """Test that stats() returns Stats object."""
        mock_response = MagicMock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "total_agents": 68,
            "total_memories": 112,
            "total_transactions": 13,
            "total_volume_botc": 96.0,
        }
        mock_get.return_value = mock_response

        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("test-agent", private_hex, public_hex)

        stats = bot.stats()

        assert stats.agents == 68
        assert stats.memories == 112

    @patch("botcoin.client.requests.get")
    def test_health_returns_api_health(self, mock_get):
        """Test that health() returns API health."""
        mock_response = MagicMock()
        mock_response.json.return_value = {"status": "healthy", "neo4j": "connected"}
        mock_get.return_value = mock_response

        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("test-agent", private_hex, public_hex)

        health = bot.health()

        assert health["status"] == "healthy"

    def test_repr_shows_agent_id(self):
        """Test string representation."""
        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("my-agent-123", private_hex, public_hex)

        assert "my-agent-123" in repr(bot)


class TestBotcoinIntegration:
    """Integration tests that hit the live API.

    These tests are marked with @pytest.mark.integration and skipped by default.
    Run with: pytest -m integration
    """

    @pytest.mark.integration
    def test_health_check_live(self):
        """Test health check against live API."""
        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("test", private_hex, public_hex)

        health = bot.health()

        assert "status" in health

    @pytest.mark.integration
    def test_stats_live(self):
        """Test stats against live API."""
        private_hex, public_hex = BotcoinAuth.generate()
        bot = Botcoin.from_credentials("test", private_hex, public_hex)

        stats = bot.stats()

        assert stats.agents > 0
