import pytest
from jwt_auth.models import LSAPIToken
from rest_framework import status
from rest_framework.test import APIClient
from rest_framework_simplejwt.exceptions import TokenError
from tests.jwt_auth.utils import create_user_with_token_settings
from tests.utils import mock_feature_flag


@mock_feature_flag(flag_name='fflag__feature_develop__prompts__dia_1829_jwt_token_auth', value=True)
@pytest.mark.django_db
def test_blacklist_view_returns_404_with_already_blacklisted_token(client):
    user = create_user_with_token_settings(api_tokens_enabled=True, legacy_api_tokens_enabled=False)
    client.force_login(user)

    token = LSAPIToken()
    token.blacklist()
    response = client.post('/api/token/blacklist/', data={'refresh': token.get_full_jwt()})

    assert response.status_code == status.HTTP_404_NOT_FOUND


@mock_feature_flag(flag_name='fflag__feature_develop__prompts__dia_1829_jwt_token_auth', value=True)
@pytest.mark.django_db
def test_blacklist_view_returns_204_with_valid_token(client):
    user = create_user_with_token_settings(api_tokens_enabled=True, legacy_api_tokens_enabled=False)
    client.force_login(user)

    token = LSAPIToken()
    response = client.post('/api/token/blacklist/', data={'refresh': token.get_full_jwt()})

    assert response.status_code == status.HTTP_204_NO_CONTENT
    with pytest.raises(TokenError):
        token.check_blacklist()


@mock_feature_flag(flag_name='fflag__feature_develop__prompts__dia_1829_jwt_token_auth', value=True)
@pytest.mark.django_db
def test_create_token_when_no_existing_token():
    user = create_user_with_token_settings(api_tokens_enabled=True, legacy_api_tokens_enabled=False)
    client = APIClient()
    refresh = LSAPIToken()
    client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')
    client.force_authenticate(user)

    response = client.post('/api/token/')

    assert response.status_code == status.HTTP_201_CREATED
    assert 'token' in response.data


@mock_feature_flag(flag_name='fflag__feature_develop__prompts__dia_1829_jwt_token_auth', value=True)
@pytest.mark.django_db
def test_create_token_when_existing_valid_token():
    user = create_user_with_token_settings(api_tokens_enabled=True, legacy_api_tokens_enabled=False)
    client = APIClient()
    refresh = LSAPIToken()
    client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')
    client.force_authenticate(user)

    # 1. Create first token
    response = client.post('/api/token/')
    assert response.status_code == status.HTTP_201_CREATED

    # 2. Try to create second token
    response = client.post('/api/token/')
    assert response.status_code == status.HTTP_409_CONFLICT
    assert 'detail' in response.data
    assert 'You already have a valid token' in response.data['detail']


@mock_feature_flag(flag_name='fflag__feature_develop__prompts__dia_1829_jwt_token_auth', value=True)
@pytest.mark.django_db
def test_create_token_after_blacklisting_previous():
    user = create_user_with_token_settings(api_tokens_enabled=True, legacy_api_tokens_enabled=False)
    client = APIClient()
    refresh = LSAPIToken()
    client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')
    client.force_authenticate(user)

    # 1. Create first token
    response = client.post('/api/token/')
    assert response.status_code == status.HTTP_201_CREATED

    # 2. Blacklist the token
    token = response.data['token']
    response = client.post('/api/token/blacklist/', data={'refresh': token})
    assert response.status_code == status.HTTP_204_NO_CONTENT

    # 3. Create new token
    response = client.post('/api/token/')
    assert response.status_code == status.HTTP_201_CREATED
    assert 'token' in response.data


@mock_feature_flag(flag_name='fflag__feature_develop__prompts__dia_1829_jwt_token_auth', value=True)
@pytest.mark.django_db
def test_rotate_token_success():
    user = create_user_with_token_settings(api_tokens_enabled=True, legacy_api_tokens_enabled=False)
    client = APIClient()
    refresh = LSAPIToken()
    client.credentials(HTTP_AUTHORIZATION=f'Bearer {refresh.access_token}')
    client.force_authenticate(user)

    # 1. Create first token
    response = client.post('/api/token/')
    assert response.status_code == status.HTTP_201_CREATED

    # 2. Rotate the token
    token = response.data['token']
    response2 = client.post('/api/token/rotate/', data={'refresh': token}, format='json')
    assert response2.status_code == status.HTTP_200_OK
    assert 'refresh' in response2.data

    # 3. The old refresh token should now be invalid
    response3 = client.post('/api/token/rotate/', data={'refresh': token}, format='json')
    assert response3.status_code == status.HTTP_400_BAD_REQUEST
    assert 'detail' in response3.data or 'non_field_errors' in response3.data

    # 4. The new refresh token should work for another rotation
    new_token = response2.data['refresh']
    response4 = client.post('/api/token/rotate/', data={'refresh': new_token}, format='json')
    assert response4.status_code == status.HTTP_200_OK
    assert 'refresh' in response4.data


@mock_feature_flag(flag_name='fflag__feature_develop__prompts__dia_1829_jwt_token_auth', value=True)
@pytest.mark.django_db
def test_rotate_token_requires_authentication():
    user = create_user_with_token_settings(api_tokens_enabled=True, legacy_api_tokens_enabled=False)
    refresh = LSAPIToken.for_user(user)
    refresh_token = refresh.get_full_jwt()

    client = APIClient()
    # No credentials set
    response = client.post('/api/token/rotate/', data={'refresh': refresh_token}, format='json')
    assert response.status_code in (status.HTTP_401_UNAUTHORIZED, status.HTTP_403_FORBIDDEN)
