import pytest
from unittest.mock import patch, MagicMock
from datetime import datetime, timedelta, timezone

from jose import jwt, JWTError
from passlib.context import CryptContext

from app.core.security import (
    verify_password,
    hash_password,
#     create_access_token,
#     create_refresh_token,
#     verify_access_token,
#     verify_refresh_token,
    pwd_context, # Import for direct testing if needed, or to check its config
)
# Assuming app.config.settings will be mocked
# from app.config import settings

# --- Tests for Password Hashing ---

def test_hash_password():
    password = "securepassword123"
    hashed = hash_password(password)
    assert isinstance(hashed, str)
    assert hashed != password
    # Check that the default scheme (bcrypt) is used by verifying the hash prefix
    # bcrypt hashes typically start with $2b$ or $2a$ or $2y$
    assert hashed.startswith("$2b$") or hashed.startswith("$2a$") or hashed.startswith("$2y$")

def test_verify_password_correct():
    password = "testpassword"
    hashed_password = pwd_context.hash(password) # Use the same context for consistency
    assert verify_password(password, hashed_password) is True

def test_verify_password_incorrect():
    password = "testpassword"
    wrong_password = "wrongpassword"
    hashed_password = pwd_context.hash(password)
    assert verify_password(wrong_password, hashed_password) is False

def test_verify_password_invalid_hash_format():
    password = "testpassword"
    invalid_hash = "notarealhash"
    assert verify_password(password, invalid_hash) is False

# --- Tests for JWT Creation ---
# Mock settings for JWT tests
# @pytest.fixture(scope="module")
# def mock_jwt_settings():
#     mock_settings = MagicMock()
#     mock_settings.SECRET_KEY = "testsecretkey"
#     mock_settings.ALGORITHM = "HS256"
#     mock_settings.ACCESS_TOKEN_EXPIRE_MINUTES = 30
#     mock_settings.REFRESH_TOKEN_EXPIRE_MINUTES = 10080 # 7 days
#     return mock_settings

# @patch('app.core.security.settings')
# def test_create_access_token_default_expiry(mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     mock_settings_global.ACCESS_TOKEN_EXPIRE_MINUTES = mock_jwt_settings.ACCESS_TOKEN_EXPIRE_MINUTES

#     subject = "user@example.com"
#     token = create_access_token(subject)
#     assert isinstance(token, str)

#     decoded_payload = jwt.decode(token, mock_jwt_settings.SECRET_KEY, algorithms=[mock_jwt_settings.ALGORITHM])
#     assert decoded_payload["sub"] == subject
#     assert decoded_payload["type"] == "access"
#     assert "exp" in decoded_payload
#     # Check if expiry is roughly correct (within a small delta)
#     expected_expiry = datetime.now(timezone.utc) + timedelta(minutes=mock_jwt_settings.ACCESS_TOKEN_EXPIRE_MINUTES)
#     assert abs(datetime.fromtimestamp(decoded_payload["exp"], timezone.utc) - expected_expiry) < timedelta(seconds=5)

# @patch('app.core.security.settings')
# def test_create_access_token_custom_expiry(mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     # ACCESS_TOKEN_EXPIRE_MINUTES is not used here due to custom delta

#     subject = 123 # Subject can be int
#     custom_delta = timedelta(hours=1)
#     token = create_access_token(subject, expires_delta=custom_delta)
#     assert isinstance(token, str)

#     decoded_payload = jwt.decode(token, mock_jwt_settings.SECRET_KEY, algorithms=[mock_jwt_settings.ALGORITHM])
#     assert decoded_payload["sub"] == str(subject)
#     assert decoded_payload["type"] == "access"
#     expected_expiry = datetime.now(timezone.utc) + custom_delta
#     assert abs(datetime.fromtimestamp(decoded_payload["exp"], timezone.utc) - expected_expiry) < timedelta(seconds=5)

# @patch('app.core.security.settings')
# def test_create_refresh_token_default_expiry(mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     mock_settings_global.REFRESH_TOKEN_EXPIRE_MINUTES = mock_jwt_settings.REFRESH_TOKEN_EXPIRE_MINUTES

#     subject = "refresh_subject"
#     token = create_refresh_token(subject)
#     assert isinstance(token, str)

#     decoded_payload = jwt.decode(token, mock_jwt_settings.SECRET_KEY, algorithms=[mock_jwt_settings.ALGORITHM])
#     assert decoded_payload["sub"] == subject
#     assert decoded_payload["type"] == "refresh"
#     expected_expiry = datetime.now(timezone.utc) + timedelta(minutes=mock_jwt_settings.REFRESH_TOKEN_EXPIRE_MINUTES)
#     assert abs(datetime.fromtimestamp(decoded_payload["exp"], timezone.utc) - expected_expiry) < timedelta(seconds=5)

# --- Tests for JWT Verification --- (More tests to be added here)

# @patch('app.core.security.settings')
# def test_verify_access_token_valid(mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     mock_settings_global.ACCESS_TOKEN_EXPIRE_MINUTES = mock_jwt_settings.ACCESS_TOKEN_EXPIRE_MINUTES

#     subject = "test_user_valid_access"
#     token = create_access_token(subject)
#     payload = verify_access_token(token)
#     assert payload is not None
#     assert payload["sub"] == subject
#     assert payload["type"] == "access"

# @patch('app.core.security.settings')
# def test_verify_access_token_invalid_signature(mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     mock_settings_global.ACCESS_TOKEN_EXPIRE_MINUTES = mock_jwt_settings.ACCESS_TOKEN_EXPIRE_MINUTES

#     subject = "test_user_invalid_sig"
#     # Create token with correct key
#     token = create_access_token(subject)

#     # Try to verify with wrong key
#     mock_settings_global.SECRET_KEY = "wrongsecretkey"
#     payload = verify_access_token(token)
#     assert payload is None

# @patch('app.core.security.settings')
# @patch('app.core.security.datetime') # Mock datetime to control token expiry
# def test_verify_access_token_expired(mock_datetime, mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     mock_settings_global.ACCESS_TOKEN_EXPIRE_MINUTES = 1 # Expire in 1 minute

#     # Set current time for token creation
#     now = datetime.now(timezone.utc)
#     mock_datetime.now.return_value = now
#     mock_datetime.fromtimestamp = datetime.fromtimestamp # Ensure original fromtimestamp is used by jwt.decode
#     mock_datetime.timedelta = timedelta # Ensure original timedelta is used

#     subject = "test_user_expired"
#     token = create_access_token(subject)

#     # Advance time beyond expiry for verification
#     mock_datetime.now.return_value = now + timedelta(minutes=5)
#     payload = verify_access_token(token)
#     assert payload is None

# @patch('app.core.security.settings')
# def test_verify_access_token_wrong_type(mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     mock_settings_global.REFRESH_TOKEN_EXPIRE_MINUTES = mock_jwt_settings.REFRESH_TOKEN_EXPIRE_MINUTES # For refresh token creation

#     subject = "test_user_wrong_type"
#     # Create a refresh token
#     refresh_token = create_refresh_token(subject)

#     # Try to verify it as an access token
#     payload = verify_access_token(refresh_token)
#     assert payload is None

# @patch('app.core.security.settings')
# def test_verify_refresh_token_valid(mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     mock_settings_global.REFRESH_TOKEN_EXPIRE_MINUTES = mock_jwt_settings.REFRESH_TOKEN_EXPIRE_MINUTES

#     subject = "test_user_valid_refresh"
#     token = create_refresh_token(subject)
#     payload = verify_refresh_token(token)
#     assert payload is not None
#     assert payload["sub"] == subject
#     assert payload["type"] == "refresh"

# @patch('app.core.security.settings')
# @patch('app.core.security.datetime')
# def test_verify_refresh_token_expired(mock_datetime, mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     mock_settings_global.REFRESH_TOKEN_EXPIRE_MINUTES = 1 # Expire in 1 minute

#     now = datetime.now(timezone.utc)
#     mock_datetime.now.return_value = now
#     mock_datetime.fromtimestamp = datetime.fromtimestamp
#     mock_datetime.timedelta = timedelta

#     subject = "test_user_expired_refresh"
#     token = create_refresh_token(subject)

#     mock_datetime.now.return_value = now + timedelta(minutes=5)
#     payload = verify_refresh_token(token)
#     assert payload is None

# @patch('app.core.security.settings')
# def test_verify_refresh_token_wrong_type(mock_settings_global, mock_jwt_settings):
#     mock_settings_global.SECRET_KEY = mock_jwt_settings.SECRET_KEY
#     mock_settings_global.ALGORITHM = mock_jwt_settings.ALGORITHM
#     mock_settings_global.ACCESS_TOKEN_EXPIRE_MINUTES = mock_jwt_settings.ACCESS_TOKEN_EXPIRE_MINUTES

#     subject = "test_user_wrong_type_refresh"
#     access_token = create_access_token(subject)

#     payload = verify_refresh_token(access_token)
#     assert payload is None 