"""
Tests for GetBijiClient.
"""

from unittest.mock import MagicMock, Mock, patch

import pytest

from getbiji_box import GetBijiClient
from getbiji_box.exceptions import APIError, AuthenticationError


class TestGetBijiClient:
    """Test cases for GetBijiClient class."""

    def test_init(self):
        """Test client initialization."""
        client = GetBijiClient(
            token="test_token",
            refresh_token="test_refresh_token",
        )
        assert client.token == "test_token"
        assert client._auto_refresh is True
        assert client._token_refresh_buffer == 300

    def test_init_without_refresh_token(self):
        """Test client initialization without refresh token."""
        client = GetBijiClient(token="test_token")
        assert client.token == "test_token"

    def test_auto_refresh_disabled(self):
        """Test client with auto refresh disabled."""
        client = GetBijiClient(
            token="test_token",
            auto_refresh=False,
        )
        assert client._auto_refresh is False

    def test_account_property(self):
        """Test account property creates AccountAPI."""
        client = GetBijiClient(token="test_token")
        account_api = client.account
        assert account_api is not None
        # Second call should return same instance
        assert account_api is client.account

    def test_notes_property(self):
        """Test notes property creates NotesAPI."""
        client = GetBijiClient(token="test_token")
        notes_api = client.notes
        assert notes_api is not None
        assert notes_api is client.notes

    def test_knowledge_property(self):
        """Test knowledge property creates KnowledgeAPI."""
        client = GetBijiClient(token="test_token")
        knowledge_api = client.knowledge
        assert knowledge_api is not None
        assert knowledge_api is client.knowledge

    @patch("getbiji_box.client.requests.Session.request")
    def test_get_request(self, mock_request):
        """Test GET request."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "h": {"c": 0},
            "c": {"data": "test"},
        }
        mock_request.return_value = mock_response

        client = GetBijiClient(token="test_token")
        result = client.get("https://test.com/endpoint")

        assert result["c"]["data"] == "test"

    @patch("getbiji_box.client.requests.Session.request")
    def test_post_request(self, mock_request):
        """Test POST request."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {
            "h": {"c": 0},
            "c": {"id": "123"},
        }
        mock_request.return_value = mock_response

        client = GetBijiClient(token="test_token")
        response = client.post("https://test.com/endpoint", json={"test": "data"})

        data = client._parse_response(response)
        assert data["c"]["id"] == "123"

    def test_parse_response_error(self):
        """Test parsing error response."""
        client = GetBijiClient(token="test_token")
        mock_response = Mock()
        mock_response.json.return_value = {
            "h": {"c": 1, "e": "Test error"},
        }

        with pytest.raises(APIError) as exc_info:
            client._parse_response(mock_response)

        assert exc_info.value.code == 1
        assert exc_info.value.message == "Test error"

    @patch("getbiji_box.client.requests.Session.request")
    def test_401_handling_with_refresh(self, mock_request):
        """Test 401 error triggers token refresh."""
        # First call returns 401, second succeeds
        mock_response_401 = Mock()
        mock_response_401.status_code = 401

        mock_response_success = Mock()
        mock_response_success.json.return_value = {
            "h": {"c": 0},
            "c": {"data": "success"},
        }
        mock_response_success.status_code = 200

        mock_request.side_effect = [mock_response_401, mock_response_success]

        client = GetBijiClient(
            token="test_token",
            refresh_token="refresh_token",
        )

        # Mock the refresh method to succeed (no-op)
        with patch.object(client, "refresh"):
            response = client._request("GET", "https://test.com/endpoint")

        assert response.status_code == 200

    def test_context_manager(self):
        """Test client as context manager."""
        with GetBijiClient(token="test_token") as client:
            assert client is not None
            assert client.token == "test_token"
        # Session should be closed after exiting

    def test_auth_manager_property(self):
        """Test auth_manager property."""
        client = GetBijiClient(token="test_token")
        assert client.auth_manager is not None

    @patch("getbiji_box.client.requests.Session.request")
    def test_refresh_method(self, mock_request):
        """Test client refresh method."""
        client = GetBijiClient(token="test_token", refresh_token="rt")
        with patch.object(client._auth_manager, "refresh") as mock_refresh:
            mock_refresh.return_value = Mock(token="new")
            client.refresh()
            mock_refresh.assert_called_once()

    @patch("getbiji_box.client.requests.Session.request")
    def test_custom_headers_in_request(self, mock_request):
        """Test custom headers are merged into request."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"h": {"c": 0}, "c": {}}
        mock_request.return_value = mock_response

        client = GetBijiClient(token="test_token")
        client.get("https://test.com/ep", headers={"X-Custom": "val"})

        call_kwargs = mock_request.call_args
        assert "X-Custom" in call_kwargs.kwargs.get("headers", {})

    @patch("getbiji_box.client.requests.Session.request")
    def test_401_refresh_fails_returns_response(self, mock_request):
        """Test 401 when refresh fails returns original response."""
        mock_response_401 = Mock()
        mock_response_401.status_code = 401

        mock_request.return_value = mock_response_401

        client = GetBijiClient(token="test_token", refresh_token="rt")
        with patch.object(client, "refresh", side_effect=AuthenticationError("fail")):
            response = client._request("GET", "https://test.com/ep")
        assert response.status_code == 401

    @patch("getbiji_box.client.requests.Session.request")
    def test_403_raises(self, mock_request):
        """Test 403 raises AuthenticationError."""
        mock_response = Mock()
        mock_response.status_code = 403
        mock_request.return_value = mock_response

        client = GetBijiClient(token="test_token", auto_refresh=False)
        with pytest.raises(AuthenticationError, match="forbidden"):
            client.get("https://test.com/ep")

    @patch("getbiji_box.client.requests.Session.request")
    def test_500_raises(self, mock_request):
        """Test 500 raises via raise_for_status."""
        mock_response = Mock()
        mock_response.status_code = 500
        mock_response.raise_for_status.side_effect = Exception("Server Error")
        mock_request.return_value = mock_response

        client = GetBijiClient(token="test_token", auto_refresh=False)
        with pytest.raises(Exception, match="Server Error"):
            client.get("https://test.com/ep")

    def test_parse_response_invalid_json(self):
        """Test parsing response with invalid JSON."""
        client = GetBijiClient(token="test_token")
        mock_response = Mock()
        mock_response.json.side_effect = ValueError("No JSON")

        with pytest.raises(APIError, match="Invalid JSON"):
            client._parse_response(mock_response)

    @patch("getbiji_box.client.requests.Session.request")
    def test_put_request(self, mock_request):
        """Test PUT request."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"h": {"c": 0}, "c": {"updated": True}}
        mock_request.return_value = mock_response

        client = GetBijiClient(token="test_token")
        result = client.put("https://test.com/ep", json={"data": "val"})
        assert result["c"]["updated"] is True

    @patch("getbiji_box.client.requests.Session.request")
    def test_delete_request(self, mock_request):
        """Test DELETE request."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"h": {"c": 0}, "c": {"deleted": True}}
        mock_request.return_value = mock_response

        client = GetBijiClient(token="test_token")
        result = client.delete("https://test.com/ep")
        assert result["c"]["deleted"] is True

    @patch("getbiji_box.client.requests.Session.request")
    def test_files_upload_removes_content_type(self, mock_request):
        """Test that file upload removes Content-Type header."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"h": {"c": 0}, "c": {}}
        mock_request.return_value = mock_response

        client = GetBijiClient(token="test_token")
        client.post("https://test.com/upload", files={"file": b"data"})

        call_kwargs = mock_request.call_args
        headers = call_kwargs.kwargs.get("headers", {})
        assert "Content-Type" not in headers

    @patch("getbiji_box.client.requests.Session.request")
    def test_401_raises_when_raise_on_401(self, mock_request):
        """Test 401 raises AuthenticationError with raise_on_401=True."""
        mock_response = Mock()
        mock_response.status_code = 401
        mock_request.return_value = mock_response

        client = GetBijiClient(token="test_token", auto_refresh=False)
        with pytest.raises(AuthenticationError, match="Authentication failed"):
            client._do_request("GET", "https://test.com/ep", raise_on_401=True)


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
