import jwt
import pytest

from bluefox_auth.settings import AuthSettings
from bluefox_auth.tokens import (
    InvalidTokenError,
    create_access_token,
    create_refresh_token,
    create_token_pair,
    decode_token,
)

SECRET = "test-secret-key-that-is-long-enough-for-validation"
SETTINGS = AuthSettings(secret_key=SECRET, access_token_expire_minutes=30)


def test_create_access_token__returns_valid_jwt():
    token = create_access_token(1, SETTINGS)
    payload = jwt.decode(token, SECRET, algorithms=["HS256"], audience="bluefox")
    assert payload["sub"] == "1"
    assert payload["type"] == "access"


def test_create_access_token__includes_jti():
    token = create_access_token(1, SETTINGS)
    payload = jwt.decode(token, SECRET, algorithms=["HS256"], audience="bluefox")
    assert "jti" in payload
    assert len(payload["jti"]) == 32  # uuid4().hex


def test_create_access_token__includes_iat():
    token = create_access_token(1, SETTINGS)
    payload = jwt.decode(token, SECRET, algorithms=["HS256"], audience="bluefox")
    assert "iat" in payload


def test_create_access_token__includes_audience():
    token = create_access_token(1, SETTINGS)
    payload = jwt.decode(token, SECRET, algorithms=["HS256"], audience="bluefox")
    assert payload["aud"] == "bluefox"


def test_create_access_token__expires_in_configured_minutes():
    token = create_access_token(1, SETTINGS)
    payload = jwt.decode(token, SECRET, algorithms=["HS256"], audience="bluefox")
    # exp - iat should be ~30 minutes (1800 seconds)
    diff = payload["exp"] - payload["iat"]
    assert diff == 1800


def test_create_access_token__unique_jti_per_call():
    t1 = create_access_token(1, SETTINGS)
    t2 = create_access_token(1, SETTINGS)
    p1 = jwt.decode(t1, SECRET, algorithms=["HS256"], audience="bluefox")
    p2 = jwt.decode(t2, SECRET, algorithms=["HS256"], audience="bluefox")
    assert p1["jti"] != p2["jti"]


def test_create_refresh_token__type_is_refresh():
    token, _jti, _family_id = create_refresh_token(1, SETTINGS)
    payload = jwt.decode(token, SECRET, algorithms=["HS256"], audience="bluefox")
    assert payload["type"] == "refresh"


def test_create_refresh_token__returns_jti_and_family_id():
    token, jti, family_id = create_refresh_token(1, SETTINGS)
    payload = jwt.decode(token, SECRET, algorithms=["HS256"], audience="bluefox")
    assert payload["jti"] == jti
    assert len(family_id) == 32  # uuid4().hex


def test_create_token_pair__returns_both_tokens():
    pair, jti, family_id = create_token_pair(1, SETTINGS)
    assert pair.access_token
    assert pair.refresh_token
    assert pair.token_type == "bearer"
    assert jti
    assert family_id


def test_decode_token__valid_access_token():
    token = create_access_token(42, SETTINGS)
    payload = decode_token(token, SETTINGS, expected_type="access")
    assert payload.sub == 42
    assert payload.type == "access"
    assert payload.jti
    assert payload.audience == "bluefox"


def test_decode_token__expired_token_raises():
    expired_settings = AuthSettings(secret_key=SECRET, access_token_expire_minutes=-1)
    token = create_access_token(1, expired_settings)
    with pytest.raises(InvalidTokenError, match="expired"):
        decode_token(token, SETTINGS, expected_type="access")


def test_decode_token__invalid_signature_raises():
    token = create_access_token(1, SETTINGS)
    other_settings = AuthSettings(secret_key="different-secret-key-that-is-long-enough")
    with pytest.raises(InvalidTokenError, match="Invalid token"):
        decode_token(token, other_settings, expected_type="access")


def test_decode_token__malformed_token_raises():
    with pytest.raises(InvalidTokenError, match="Invalid token"):
        decode_token("not-a-jwt", SETTINGS, expected_type="access")


def test_decode_token__wrong_type_raises():
    token = create_access_token(1, SETTINGS)
    with pytest.raises(InvalidTokenError, match="Expected token type"):
        decode_token(token, SETTINGS, expected_type="refresh")


def test_decode_token__wrong_audience_raises():
    token = create_access_token(1, SETTINGS)
    other_settings = AuthSettings(secret_key=SECRET, token_audience="other-app")
    with pytest.raises(InvalidTokenError, match="audience"):
        decode_token(token, other_settings, expected_type="access")
