"""
Tests for SkillBrokerClient.
"""

import pytest
from unittest.mock import Mock, patch, MagicMock
import requests

from skillbroker_langchain import (
    SkillBrokerClient,
    SkillBrokerError,
    SkillNotFoundError,
    InvocationError,
    Skill,
    SkillResponse,
    SearchResult,
)


class TestSkillBrokerClient:
    """Tests for the SkillBrokerClient class."""

    def test_init_default_url(self):
        """Test client initializes with default API URL."""
        client = SkillBrokerClient()
        assert client.api_url == "https://api.skillbroker.io"
        assert client.timeout == 30

    def test_init_custom_url(self):
        """Test client accepts custom API URL."""
        client = SkillBrokerClient(api_url="https://custom.api.io")
        assert client.api_url == "https://custom.api.io"

    def test_init_with_api_key(self):
        """Test client sets authorization header with API key."""
        client = SkillBrokerClient(api_key="test-key")
        assert client._session.headers["Authorization"] == "Bearer test-key"

    @patch.object(requests.Session, 'request')
    def test_get_registry_info(self, mock_request):
        """Test getting registry info."""
        mock_response = Mock()
        mock_response.json.return_value = {
            "stats": {"totalSkills": 42, "totalCategories": 5}
        }
        mock_response.raise_for_status = Mock()
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        result = client.get_registry_info()

        assert result["stats"]["totalSkills"] == 42
        mock_request.assert_called_once()

    @patch.object(requests.Session, 'request')
    def test_search_skills(self, mock_request):
        """Test searching for skills."""
        mock_response = Mock()
        mock_response.json.return_value = {
            "skills": [
                {
                    "id": "tax-advisor",
                    "name": "Tax Advisor",
                    "description": "Expert tax advice",
                    "version": "1.0.0",
                    "author": "expert@example.com",
                    "category": "Finance",
                }
            ]
        }
        mock_response.raise_for_status = Mock()
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        result = client.search(query="tax", limit=5)

        assert isinstance(result, SearchResult)
        assert len(result.skills) == 1
        assert result.skills[0].id == "tax-advisor"
        assert result.query == "tax"

    @patch.object(requests.Session, 'request')
    def test_search_with_category(self, mock_request):
        """Test searching with category filter."""
        mock_response = Mock()
        mock_response.json.return_value = {"skills": []}
        mock_response.raise_for_status = Mock()
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        client.search(query="advice", category="Finance")

        # Verify the params were passed correctly
        call_args = mock_request.call_args
        assert call_args[1]["params"]["category"] == "Finance"

    @patch.object(requests.Session, 'request')
    def test_get_skill(self, mock_request):
        """Test getting a skill by ID."""
        mock_response = Mock()
        mock_response.json.return_value = {
            "id": "test-skill",
            "name": "Test Skill",
            "description": "A test skill",
            "version": "1.0.0",
            "author": "test@example.com",
            "category": "Test",
        }
        mock_response.raise_for_status = Mock()
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        skill = client.get_skill("test-skill")

        assert isinstance(skill, Skill)
        assert skill.id == "test-skill"
        assert skill.name == "Test Skill"

    @patch.object(requests.Session, 'request')
    def test_get_skill_not_found(self, mock_request):
        """Test 404 raises SkillNotFoundError."""
        mock_response = Mock()
        mock_response.status_code = 404
        http_error = requests.exceptions.HTTPError(response=mock_response)
        mock_response.raise_for_status.side_effect = http_error
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        with pytest.raises(SkillNotFoundError):
            client.get_skill("nonexistent")

    @patch.object(requests.Session, 'request')
    def test_invoke_skill(self, mock_request):
        """Test invoking a skill."""
        mock_response = Mock()
        mock_response.json.return_value = {
            "response": "Here is your answer...",
            "skillName": "Tax Advisor",
            "tokensUsed": 150,
            "cost": 0.001,
        }
        mock_response.raise_for_status = Mock()
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        result = client.invoke("tax-advisor", "Can I deduct home office?")

        assert isinstance(result, SkillResponse)
        assert result.success is True
        assert result.response == "Here is your answer..."
        assert result.skill_id == "tax-advisor"
        assert result.tokens_used == 150

    @patch.object(requests.Session, 'request')
    def test_invoke_with_context(self, mock_request):
        """Test invoking with context."""
        mock_response = Mock()
        mock_response.json.return_value = {"response": "Answer with context"}
        mock_response.raise_for_status = Mock()
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        client.invoke("skill-id", "query", context={"user_type": "freelancer"})

        call_args = mock_request.call_args
        assert call_args[1]["json"]["context"] == {"user_type": "freelancer"}

    @patch.object(requests.Session, 'request')
    def test_invoke_error(self, mock_request):
        """Test invocation error handling."""
        mock_response = Mock()
        mock_response.status_code = 500
        http_error = requests.exceptions.HTTPError(response=mock_response)
        mock_response.raise_for_status.side_effect = http_error
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        with pytest.raises(InvocationError):
            client.invoke("broken-skill", "query")

    @patch.object(requests.Session, 'request')
    def test_get_categories(self, mock_request):
        """Test getting categories."""
        mock_response = Mock()
        mock_response.json.return_value = [
            {"id": "finance", "name": "Finance", "count": 10},
            {"id": "ai", "name": "AI & ML", "count": 15},
        ]
        mock_response.raise_for_status = Mock()
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        categories = client.get_categories()

        assert len(categories) == 2
        assert categories[0]["name"] == "Finance"

    @patch.object(requests.Session, 'request')
    def test_get_top_skills(self, mock_request):
        """Test getting top skills from leaderboard."""
        mock_response = Mock()
        mock_response.json.return_value = {
            "skills": [
                {
                    "id": "top-skill",
                    "name": "Top Skill",
                    "description": "Best skill",
                    "version": "1.0.0",
                    "author": "expert@example.com",
                    "category": "Finance",
                }
            ]
        }
        mock_response.raise_for_status = Mock()
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        skills = client.get_top_skills(limit=5)

        assert len(skills) == 1
        assert skills[0].id == "top-skill"

    @patch.object(requests.Session, 'request')
    def test_get_recommendations(self, mock_request):
        """Test getting skill recommendations."""
        mock_response = Mock()
        mock_response.json.return_value = {
            "recommendations": [
                {
                    "id": "rec-skill",
                    "name": "Recommended Skill",
                    "description": "Great for your task",
                    "version": "1.0.0",
                    "author": "expert@example.com",
                    "category": "AI",
                }
            ]
        }
        mock_response.raise_for_status = Mock()
        mock_request.return_value = mock_response

        client = SkillBrokerClient()
        skills = client.get_recommendations("I need help with taxes")

        assert len(skills) == 1
        assert skills[0].id == "rec-skill"

    @patch.object(requests.Session, 'request')
    def test_request_timeout(self, mock_request):
        """Test request timeout handling."""
        mock_request.side_effect = requests.exceptions.Timeout()

        client = SkillBrokerClient()
        with pytest.raises(SkillBrokerError, match="Request failed"):
            client.get_registry_info()

    @patch.object(requests.Session, 'request')
    def test_connection_error(self, mock_request):
        """Test connection error handling."""
        mock_request.side_effect = requests.exceptions.ConnectionError()

        client = SkillBrokerClient()
        with pytest.raises(SkillBrokerError, match="Request failed"):
            client.get_registry_info()
