"""Tests for SHIP HTTP client."""

import json
import pytest
from unittest.mock import patch, MagicMock

import httpx

from ship_crewai.client import SHIPClient, DEFAULT_BASE_URL, DEFAULT_TIMEOUT


class TestSHIPClientInit:
    """Tests for client initialization."""

    def test_default_config(self):
        """Test default client configuration."""
        client = SHIPClient()
        assert client.base_url == DEFAULT_BASE_URL
        assert client.api_key is None
        assert client.timeout == DEFAULT_TIMEOUT

    def test_custom_config(self):
        """Test custom client configuration."""
        client = SHIPClient(
            base_url="https://custom.api.dev",
            api_key="test-key",
            timeout=60.0,
        )
        assert client.base_url == "https://custom.api.dev"
        assert client.api_key == "test-key"
        assert client.timeout == 60.0

    def test_trailing_slash_stripped(self):
        """Test trailing slash is stripped from base URL."""
        client = SHIPClient(base_url="https://api.dev/")
        assert client.base_url == "https://api.dev"

    def test_headers_without_api_key(self):
        """Test headers when no API key is set."""
        client = SHIPClient()
        headers = client._get_headers()
        assert headers["Content-Type"] == "application/json"
        assert headers["User-Agent"] == "ship-crewai/1.0.0"
        assert "Authorization" not in headers

    def test_headers_with_api_key(self):
        """Test headers include auth when API key is set."""
        client = SHIPClient(api_key="my-secret-key")
        headers = client._get_headers()
        assert headers["Authorization"] == "Bearer my-secret-key"


class TestSHIPClientScore:
    """Tests for the score endpoint."""

    @patch("ship_crewai.client.httpx.Client")
    def test_score_success(self, mock_httpx_class, mock_score_response):
        """Test successful score request."""
        mock_response = MagicMock()
        mock_response.json.return_value = mock_score_response
        mock_response.raise_for_status.return_value = None

        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_http.post.return_value = mock_response
        mock_httpx_class.return_value = mock_http

        client = SHIPClient()
        result = client.score("facebook/react")

        assert result["score"] == 85
        assert result["grade"] == "A"
        mock_http.post.assert_called_once_with(
            "/v2/score",
            json={"repo": "facebook/react"},
        )

    @patch("ship_crewai.client.httpx.Client")
    def test_score_http_error(self, mock_httpx_class):
        """Test score raises on HTTP error."""
        mock_response = MagicMock()
        mock_response.raise_for_status.side_effect = httpx.HTTPStatusError(
            "Not Found",
            request=MagicMock(),
            response=MagicMock(status_code=404),
        )

        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_http.post.return_value = mock_response
        mock_httpx_class.return_value = mock_http

        client = SHIPClient()
        with pytest.raises(httpx.HTTPStatusError):
            client.score("nonexistent/repo")

    @patch("ship_crewai.client.httpx.Client")
    def test_score_timeout(self, mock_httpx_class):
        """Test score raises on timeout."""
        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_http.post.side_effect = httpx.TimeoutException("Request timed out")
        mock_httpx_class.return_value = mock_http

        client = SHIPClient()
        with pytest.raises(httpx.TimeoutException):
            client.score("owner/repo")


class TestSHIPClientDetect:
    """Tests for the detect endpoint."""

    @patch("ship_crewai.client.httpx.Client")
    def test_detect_success(self, mock_httpx_class, mock_detect_response):
        """Test successful detect request."""
        mock_response = MagicMock()
        mock_response.json.return_value = mock_detect_response
        mock_response.raise_for_status.return_value = None

        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_http.post.return_value = mock_response
        mock_httpx_class.return_value = mock_http

        client = SHIPClient()
        result = client.detect("def hello(): pass", "python")

        assert result["ai_probability"] == 0.87
        assert result["is_ai_generated"] is True
        mock_http.post.assert_called_once_with(
            "/v2/detect",
            json={"code": "def hello(): pass", "language": "python"},
        )

    @patch("ship_crewai.client.httpx.Client")
    def test_detect_with_language(self, mock_httpx_class):
        """Test detect passes language correctly."""
        mock_response = MagicMock()
        mock_response.json.return_value = {"ai_probability": 0.5}
        mock_response.raise_for_status.return_value = None

        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_http.post.return_value = mock_response
        mock_httpx_class.return_value = mock_http

        client = SHIPClient()
        client.detect("const x = 1;", "typescript")

        mock_http.post.assert_called_once_with(
            "/v2/detect",
            json={"code": "const x = 1;", "language": "typescript"},
        )

    @patch("ship_crewai.client.httpx.Client")
    def test_detect_http_error(self, mock_httpx_class):
        """Test detect raises on HTTP error."""
        mock_response = MagicMock()
        mock_response.raise_for_status.side_effect = httpx.HTTPStatusError(
            "Internal Server Error",
            request=MagicMock(),
            response=MagicMock(status_code=500),
        )

        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_http.post.return_value = mock_response
        mock_httpx_class.return_value = mock_http

        client = SHIPClient()
        with pytest.raises(httpx.HTTPStatusError):
            client.detect("code", "python")


class TestSHIPClientHealth:
    """Tests for the health endpoint."""

    @patch("ship_crewai.client.httpx.Client")
    def test_health_success(self, mock_httpx_class, mock_health_response):
        """Test successful health check."""
        mock_response = MagicMock()
        mock_response.json.return_value = mock_health_response
        mock_response.raise_for_status.return_value = None

        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_http.get.return_value = mock_response
        mock_httpx_class.return_value = mock_http

        client = SHIPClient()
        result = client.health()

        assert result["status"] == "ok"
        assert result["version"] == "2.0.0"
        mock_http.get.assert_called_once_with("/health")

    @patch("ship_crewai.client.httpx.Client")
    def test_health_uses_get(self, mock_httpx_class):
        """Test health endpoint uses GET, not POST."""
        mock_response = MagicMock()
        mock_response.json.return_value = {"status": "ok"}
        mock_response.raise_for_status.return_value = None

        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_http.get.return_value = mock_response
        mock_httpx_class.return_value = mock_http

        client = SHIPClient()
        client.health()

        mock_http.get.assert_called_once()
        mock_http.post.assert_not_called()

    @patch("ship_crewai.client.httpx.Client")
    def test_health_connection_error(self, mock_httpx_class):
        """Test health raises on connection error."""
        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_http.get.side_effect = httpx.ConnectError("Connection refused")
        mock_httpx_class.return_value = mock_http

        client = SHIPClient()
        with pytest.raises(httpx.ConnectError):
            client.health()


class TestSHIPClientIntegration:
    """Integration-style tests for client behavior."""

    @patch("ship_crewai.client.httpx.Client")
    def test_client_sets_correct_headers(self, mock_httpx_class):
        """Test client passes correct headers to httpx."""
        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_response = MagicMock()
        mock_response.json.return_value = {"status": "ok"}
        mock_response.raise_for_status.return_value = None
        mock_http.get.return_value = mock_response
        mock_httpx_class.return_value = mock_http

        client = SHIPClient(api_key="test-key-abc")
        client.health()

        # Verify httpx.Client was created with correct params
        call_kwargs = mock_httpx_class.call_args[1]
        assert call_kwargs["base_url"] == DEFAULT_BASE_URL
        assert call_kwargs["timeout"] == DEFAULT_TIMEOUT
        assert call_kwargs["headers"]["Authorization"] == "Bearer test-key-abc"
        assert call_kwargs["headers"]["User-Agent"] == "ship-crewai/1.0.0"

    @patch("ship_crewai.client.httpx.Client")
    def test_client_custom_timeout(self, mock_httpx_class):
        """Test custom timeout is passed to httpx."""
        mock_http = MagicMock()
        mock_http.__enter__ = MagicMock(return_value=mock_http)
        mock_http.__exit__ = MagicMock(return_value=False)
        mock_response = MagicMock()
        mock_response.json.return_value = {"status": "ok"}
        mock_response.raise_for_status.return_value = None
        mock_http.get.return_value = mock_response
        mock_httpx_class.return_value = mock_http

        client = SHIPClient(timeout=120.0)
        client.health()

        call_kwargs = mock_httpx_class.call_args[1]
        assert call_kwargs["timeout"] == 120.0
