import jwt as pyjwt
import pytest
from fastapi import HTTPException

from app.auth.jwt_provider import JWTAuthProvider

SECRET = "test-secret-key"
USERNAME = "owner"
PASSWORD = "hunter2"


def make_provider(**kwargs) -> JWTAuthProvider:
    defaults = dict(
        secret_key=SECRET,
        expiry_seconds=3600,
        owner_username=USERNAME,
        owner_password=PASSWORD,
    )
    return JWTAuthProvider(**{**defaults, **kwargs})


def test_create_token_is_valid_jwt():
    provider = make_provider()
    token = provider.create_token()
    payload = pyjwt.decode(token, SECRET, algorithms=["HS256"])
    assert payload["sub"] == "owner"
    assert "iat" in payload
    assert "exp" in payload


@pytest.mark.asyncio
async def test_get_identity_returns_owner():
    provider = make_provider()
    token = provider.create_token()
    identity = await provider.get_identity(f"Bearer {token}")
    assert identity.id == "owner"
    assert identity.anonymous is False


@pytest.mark.asyncio
async def test_get_identity_raises_on_expired_token():
    provider = make_provider(expiry_seconds=-1)
    token = provider.create_token()
    with pytest.raises(HTTPException) as exc_info:
        await provider.get_identity(f"Bearer {token}")
    assert exc_info.value.status_code == 401


@pytest.mark.asyncio
async def test_get_identity_raises_on_wrong_key():
    provider = make_provider()
    other = make_provider(secret_key="different-secret")
    token = other.create_token()
    with pytest.raises(HTTPException) as exc_info:
        await provider.get_identity(f"Bearer {token}")
    assert exc_info.value.status_code == 401


@pytest.mark.asyncio
async def test_get_identity_raises_on_garbage():
    provider = make_provider()
    with pytest.raises(HTTPException) as exc_info:
        await provider.get_identity("Bearer not.a.real.token")
    assert exc_info.value.status_code == 401


@pytest.mark.asyncio
async def test_get_identity_raises_on_missing_header():
    provider = make_provider()
    with pytest.raises(HTTPException) as exc_info:
        await provider.get_identity(None)
    assert exc_info.value.status_code == 401


@pytest.mark.asyncio
async def test_get_identity_raises_on_missing_bearer_prefix():
    provider = make_provider()
    token = provider.create_token()
    with pytest.raises(HTTPException) as exc_info:
        await provider.get_identity(token)
    assert exc_info.value.status_code == 401


def test_verify_credentials_true():
    provider = make_provider()
    assert provider.verify_credentials(USERNAME, PASSWORD) is True


def test_verify_credentials_false_wrong_password():
    provider = make_provider()
    assert provider.verify_credentials(USERNAME, "wrongpassword") is False


def test_verify_credentials_false_wrong_username():
    provider = make_provider()
    assert provider.verify_credentials("notowner", PASSWORD) is False
