"""Unit tests for security module"""

import pytest
from datetime import datetime, timedelta
from jose import jwt
from unittest.mock import MagicMock

from app.core.security import (
    create_access_token,
    create_refresh_token,
    verify_password,
    get_password_hash,
)
from app.core.config import settings


class TestPasswordHashing:
    """Tests for password hashing functions"""

    def test_hash_password(self):
        """Test password hashing"""
        password = "test_password_123"
        hashed = get_password_hash(password)
        assert hashed != password
        assert len(hashed) > 0

    def test_verify_correct_password(self):
        """Test verification of correct password"""
        password = "test_password_123"
        hashed = get_password_hash(password)
        assert verify_password(password, hashed) is True

    def test_verify_incorrect_password(self):
        """Test verification of incorrect password"""
        password = "test_password_123"
        hashed = get_password_hash(password)
        assert verify_password("wrong_password", hashed) is False

    def test_hash_is_unique(self):
        """Test that hashes are unique for same password"""
        password = "test_password_123"
        hash1 = get_password_hash(password)
        hash2 = get_password_hash(password)
        assert hash1 != hash2  # bcrypt adds salt


class TestTokenCreation:
    """Tests for token creation functions"""

    def test_create_access_token(self):
        """Test access token creation"""
        data = {"sub": "123", "username": "testuser"}
        token = create_access_token(data)
        assert token is not None
        assert len(token) > 0

        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        assert payload["sub"] == "123"
        assert payload["username"] == "testuser"
        assert payload["type"] == "access"

    def test_create_refresh_token(self):
        """Test refresh token creation"""
        data = {"sub": "123"}
        token = create_refresh_token(data)
        assert token is not None

        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        assert payload["sub"] == "123"
        assert payload["type"] == "refresh"

    def test_access_token_expiration(self):
        """Test access token has correct expiration"""
        data = {"sub": "123"}
        token = create_access_token(data)
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        exp_timestamp = payload["exp"]
        # Token should expire in approximately 15 minutes (accounting for timezone)
        expected_minutes = settings.ACCESS_TOKEN_EXPIRE_MINUTES
        # The timestamp is in seconds since epoch
        import time

        now_timestamp = time.time()
        minutes_diff = (exp_timestamp - now_timestamp) / 60
        assert expected_minutes - 1 < minutes_diff < expected_minutes + 1

    def test_refresh_token_expiration(self):
        """Test refresh token has correct expiration"""
        data = {"sub": "123"}
        token = create_refresh_token(data)
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
        exp = datetime.fromtimestamp(payload["exp"])
        now = datetime.utcnow()
        # Token should expire in approximately 7 days (with some tolerance)
        delta = exp - now
        assert delta.days >= 6  # At least 6 days
        assert delta.days <= 8  # Less than 8 days


class TestJWTSecurity:
    """Tests for JWT security features"""

    def test_invalid_token_raises_error(self):
        """Test that invalid token raises JWTError"""
        with pytest.raises(jwt.JWTError):
            jwt.decode("invalid_token", settings.SECRET_KEY, algorithms=[settings.ALGORITHM])

    def test_token_with_wrong_secret_raises_error(self):
        """Test that token with wrong secret raises error"""
        data = {"sub": "123"}
        token = create_access_token(data)
        with pytest.raises(jwt.JWTError):
            jwt.decode(token, "wrong_secret", algorithms=[settings.ALGORITHM])
