import os
import pytest
from datetime import datetime, timedelta, timezone
from fastapi import HTTPException
from auth import (
    create_access_token,
    create_refresh_token,
    _decode_token,
    verify_refresh_token,
    hash_token,
    JWT_SECRET,
    JWT_ALGORITHM,
    ACCESS_TOKEN_EXPIRE_MINUTES,
    REFRESH_TOKEN_EXPIRE_DAYS,
)
import jwt

def test_create_access_token():
    token = create_access_token(entity_id=1, email="test@example.com")
    assert isinstance(token, str)
    
    payload = _decode_token(token)
    assert payload["sub"] == "1"
    assert payload["email"] == "test@example.com"
    assert payload["type"] == "access"
    
def test_create_refresh_token():
    token = create_refresh_token(entity_id=1)
    assert isinstance(token, str)
    
    payload = _decode_token(token)
    assert payload["sub"] == "1"
    assert payload["type"] == "refresh"
    assert "jti" in payload
    
def test_verify_refresh_token():
    token = create_refresh_token(entity_id=1)
    payload = verify_refresh_token(token)
    assert payload["sub"] == "1"
    assert payload["type"] == "refresh"

def test_verify_refresh_token_with_access_token():
    token = create_access_token(entity_id=1, email="test@example.com")
    with pytest.raises(HTTPException) as exc_info:
        verify_refresh_token(token)
    assert exc_info.value.status_code == 401
    assert "Invalid token type" in exc_info.value.detail

def test_expired_token():
    # Create a token that expired 1 hour ago
    payload = {
        "sub": "1",
        "email": "test@example.com",
        "type": "access",
        "iat": datetime.now(timezone.utc) - timedelta(hours=2),
        "exp": datetime.now(timezone.utc) - timedelta(hours=1),
    }
    token = jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM)
    
    with pytest.raises(HTTPException) as exc_info:
        _decode_token(token)
    assert exc_info.value.status_code == 401
    assert "Token has expired" in exc_info.value.detail

def test_invalid_signature():
    token = create_access_token(entity_id=1, email="test@example.com")
    
    # Tamper with the token
    parts = token.split(".")
    parts[2] = "invalid_signature"
    tampered_token = ".".join(parts)
    
    with pytest.raises(HTTPException) as exc_info:
        _decode_token(tampered_token)
    assert exc_info.value.status_code == 401
    assert "Invalid token" in exc_info.value.detail

def test_none_algorithm_rejected():
    # Attempt to bypass signature verification using "none" algorithm
    payload = {
        "sub": "1",
        "email": "test@example.com",
        "type": "access",
        "iat": int((datetime.now(timezone.utc)).timestamp()),
        "exp": int((datetime.now(timezone.utc) + timedelta(hours=1)).timestamp()),
    }
    
    # Create a token with 'none' alg
    # This requires constructing the JWT manually as PyJWT correctly refuses to create 'none' alg tokens with a secret
    import base64
    import json
    
    header = {"alg": "none", "typ": "JWT"}
    b64_header = base64.urlsafe_b64encode(json.dumps(header).encode()).decode().rstrip("=")
    b64_payload = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().rstrip("=")
    
    none_token = f"{b64_header}.{b64_payload}."
    
    with pytest.raises(HTTPException) as exc_info:
        _decode_token(none_token)
    assert exc_info.value.status_code == 401
    assert "Invalid token" in exc_info.value.detail

def test_hash_token():
    token = "test_token_string"
    hashed = hash_token(token)
    assert isinstance(hashed, str)
    assert len(hashed) == 64 # SHA-256 hex digest length
    assert hash_token(token) == hashed # deterministic
