import pytest
from unittest.mock import Mock, patch
import requests
from notifiedby.client import NotifiedByClient
from notifiedby.exceptions import (
    NotifiedByConfigError,
    NotifiedByAPIError,
    NotifiedByValidationError,
    NotifiedByAuthError,
    NotifiedByNotFoundError,
    NotifiedByError,
)


def test_client_init_with_api_key():
    """Test client initialization with explicit API key."""
    client = NotifiedByClient(api_key='test_key')
    assert client.api_key == 'test_key'
    assert client.base_url == 'https://api.notifiedby.com'
    assert client.encryption_key is None


def test_client_init_from_settings(mock_settings):
    """Test client initialization from Django settings."""
    client = NotifiedByClient()
    assert client.api_key == 'test_api_key'


def test_client_init_without_api_key(monkeypatch):
    """Test client raises error when API key not configured."""
    # Remove API key from settings
    from django.conf import settings
    monkeypatch.setattr(settings, 'NOTIFIEDBY_API_KEY', None)

    with pytest.raises(NotifiedByConfigError) as exc_info:
        NotifiedByClient()
    assert "NOTIFIEDBY_API_KEY is not set" in str(exc_info.value)


def test_client_custom_base_url():
    """Test client with custom base URL."""
    client = NotifiedByClient(api_key='test', base_url='http://localhost:8000')
    assert client.base_url == 'http://localhost:8000'


def test_get_headers(mock_settings):
    """Test _get_headers builds correct authorization headers."""
    client = NotifiedByClient()
    headers = client._get_headers()

    assert headers['Authorization'] == 'Api-Key test_api_key'
    assert 'Encryption-Key' not in headers


def test_get_headers_with_encryption(mock_settings, monkeypatch):
    """Test _get_headers includes encryption key when configured."""
    from django.conf import settings
    monkeypatch.setattr(settings, 'NOTIFIEDBY_ENCRYPTION_KEY', 'encrypt123')

    client = NotifiedByClient()
    headers = client._get_headers()

    assert headers['Authorization'] == 'Api-Key test_api_key'
    assert headers['Encryption-Key'] == 'encrypt123'


@patch('requests.request')
def test_request_success_with_json(mock_request, mock_settings):
    """Test successful request with JSON response."""
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.content = b'{"result": "success"}'
    mock_response.json.return_value = {'result': 'success'}
    mock_request.return_value = mock_response

    client = NotifiedByClient()
    result = client._request('GET', '/test/')

    assert result == {'result': 'success'}
    mock_request.assert_called_once()
    call_kwargs = mock_request.call_args.kwargs
    assert call_kwargs['headers']['Authorization'] == 'Api-Key test_api_key'
    assert call_kwargs['timeout'] == 30


@patch('requests.request')
def test_request_success_no_content(mock_request, mock_settings):
    """Test successful request with no content."""
    mock_response = Mock()
    mock_response.status_code = 204
    mock_response.content = b''
    mock_request.return_value = mock_response

    client = NotifiedByClient()
    result = client._request('DELETE', '/test/')

    assert result == {}


@patch('requests.request')
def test_request_400_validation_error(mock_request, mock_settings):
    """Test request raises NotifiedByValidationError on 400."""
    mock_response = Mock()
    mock_response.status_code = 400
    mock_response.text = '{"detail": "Invalid data"}'
    mock_request.return_value = mock_response

    client = NotifiedByClient()
    with pytest.raises(NotifiedByValidationError) as exc_info:
        client._request('POST', '/test/')

    assert exc_info.value.status_code == 400
    assert exc_info.value.endpoint == '/test/'


@patch('requests.request')
def test_request_401_auth_error(mock_request, mock_settings):
    """Test request raises NotifiedByAuthError on 401."""
    mock_response = Mock()
    mock_response.status_code = 401
    mock_response.text = 'Unauthorized'
    mock_request.return_value = mock_response

    client = NotifiedByClient()
    with pytest.raises(NotifiedByAuthError) as exc_info:
        client._request('GET', '/test/')

    assert exc_info.value.status_code == 401


@patch('requests.request')
def test_request_404_not_found_error(mock_request, mock_settings):
    """Test request raises NotifiedByNotFoundError on 404."""
    mock_response = Mock()
    mock_response.status_code = 404
    mock_response.text = 'Not found'
    mock_request.return_value = mock_response

    client = NotifiedByClient()
    with pytest.raises(NotifiedByNotFoundError) as exc_info:
        client._request('GET', '/test/')

    assert exc_info.value.status_code == 404


@patch('requests.request')
def test_request_500_api_error(mock_request, mock_settings):
    """Test request raises NotifiedByAPIError on 500."""
    mock_response = Mock()
    mock_response.status_code = 500
    mock_response.text = 'Server error'
    mock_request.return_value = mock_response

    client = NotifiedByClient()
    with pytest.raises(NotifiedByAPIError) as exc_info:
        client._request('GET', '/test/')

    assert exc_info.value.status_code == 500


@patch('requests.request')
def test_request_connection_error(mock_request, mock_settings):
    """Test request raises NotifiedByError on connection failure."""
    mock_request.side_effect = requests.exceptions.ConnectionError("Connection failed")

    client = NotifiedByClient()
    with pytest.raises(NotifiedByError) as exc_info:
        client._request('GET', '/test/')

    assert "Request failed" in str(exc_info.value)


@patch('requests.request')
def test_get_method(mock_request, mock_settings):
    """Test GET convenience method."""
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.content = b'{"data": "test"}'
    mock_response.json.return_value = {'data': 'test'}
    mock_request.return_value = mock_response

    client = NotifiedByClient()
    result = client.get('/test/', params={'page': 1})

    assert result == {'data': 'test'}
    call_kwargs = mock_request.call_args.kwargs
    assert call_kwargs['params'] == {'page': 1}


@patch('requests.request')
def test_post_method(mock_request, mock_settings):
    """Test POST convenience method."""
    mock_response = Mock()
    mock_response.status_code = 201
    mock_response.content = b'{"id": 123}'
    mock_response.json.return_value = {'id': 123}
    mock_request.return_value = mock_response

    client = NotifiedByClient()
    result = client.post('/test/', json={'name': 'test'})

    assert result == {'id': 123}
    call_kwargs = mock_request.call_args.kwargs
    assert call_kwargs['json'] == {'name': 'test'}


@patch('requests.request')
def test_delete_method(mock_request, mock_settings):
    """Test DELETE convenience method."""
    mock_response = Mock()
    mock_response.status_code = 204
    mock_response.content = b''
    mock_request.return_value = mock_response

    client = NotifiedByClient()
    result = client.delete('/test/')

    assert result == {}
