"""High priority authentication API tests."""

from __future__ import annotations

from datetime import datetime, timedelta
from typing import TYPE_CHECKING

import pytest
from fastapi.testclient import TestClient

if TYPE_CHECKING:
    from sqlalchemy.orm import Session


class TestLogin:
    """Test login endpoint."""

    def test_login_success_with_valid_credentials(
        self, test_client: TestClient, admin_user
    ):
        """Verify successful login with valid username/password."""
        response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "testadmin", "password": "testpass"}
        )
        
        assert response.status_code == 200
        data = response.json()
        assert "access_token" in data
        assert "refresh_token" in data
        assert "password_change_required" in data
        assert isinstance(data["access_token"], str)
        assert isinstance(data["refresh_token"], str)
        assert len(data["access_token"]) > 0
        assert len(data["refresh_token"]) > 0

    def test_login_failure_with_invalid_credentials(
        self, test_client: TestClient, admin_user
    ):
        """Verify 401 with invalid credentials."""
        response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "testadmin", "password": "wrongpassword"}
        )
        
        assert response.status_code == 401
        data = response.json()
        assert "detail" in data
        assert "Incorrect username or password" in data["detail"]

    def test_login_with_inactive_user(
        self, test_client: TestClient, inactive_user
    ):
        """Verify 401 when user account is inactive."""
        response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "inactiveuser", "password": "testpass"}
        )
        
        assert response.status_code == 401
        data = response.json()
        assert "detail" in data
        assert "Account is inactive" in data["detail"]

    def test_login_fallback_to_hardcoded_admin(
        self, test_client: TestClient
    ):
        """Verify fallback to admin/admin works when user not in database."""
        # Use default hardcoded admin credentials
        response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "admin", "password": "admin"}
        )
        
        assert response.status_code == 200
        data = response.json()
        assert "access_token" in data
        assert "refresh_token" in data
        assert data["password_change_required"] is False

    def test_login_updates_last_login(
        self, test_client: TestClient, test_db_session: Session, admin_user
    ):
        """Verify last_login timestamp is updated on successful login."""
        initial_last_login = admin_user.last_login
        
        # Wait a moment to ensure timestamp difference
        import time
        time.sleep(0.1)
        
        response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "testadmin", "password": "testpass"}
        )
        
        assert response.status_code == 200
        
        # Refresh user from database
        test_db_session.refresh(admin_user)
        
        # Verify last_login was updated
        assert admin_user.last_login is not None
        if initial_last_login:
            assert admin_user.last_login > initial_last_login

    def test_login_missing_username(self, test_client: TestClient):
        """Verify 422 when username is missing."""
        response = test_client.post(
            "/api/v1/auth/login",
            json={"password": "testpass"}
        )
        
        assert response.status_code == 422

    def test_login_missing_password(self, test_client: TestClient):
        """Verify 422 when password is missing."""
        response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "testadmin"}
        )
        
        assert response.status_code == 422


class TestTokenRefresh:
    """Test token refresh endpoint."""

    def test_refresh_token_success(
        self, test_client: TestClient, admin_user
    ):
        """Verify successful token refresh."""
        # Get refresh token from login
        login_response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "testadmin", "password": "testpass"}
        )
        assert login_response.status_code == 200
        login_data = login_response.json()
        initial_access_token = login_data["access_token"]
        refresh_token = login_data["refresh_token"]
        
        # Use refresh token to get new access token
        response = test_client.post(
            "/api/v1/auth/refresh",
            json={"refresh_token": refresh_token}
        )
        
        assert response.status_code == 200
        data = response.json()
        assert "access_token" in data
        assert "refresh_token" in data
        assert data["access_token"] != initial_access_token  # Should be different token

    def test_refresh_token_with_invalid_token(self, test_client: TestClient):
        """Verify 401 with invalid refresh token."""
        response = test_client.post(
            "/api/v1/auth/refresh",
            json={"refresh_token": "invalid_token"}
        )
        
        assert response.status_code == 401
        data = response.json()
        assert "detail" in data
        assert "Invalid refresh token" in data["detail"]

    def test_refresh_token_with_access_token(
        self, test_client: TestClient, auth_token: str
    ):
        """Verify 401 when using access token instead of refresh token."""
        response = test_client.post(
            "/api/v1/auth/refresh",
            json={"refresh_token": auth_token}  # Using access token
        )
        
        assert response.status_code == 401
        data = response.json()
        assert "detail" in data
        assert "Invalid token type" in data["detail"]

    def test_refresh_token_expired(self, test_client: TestClient):
        """Verify 401 with expired refresh token."""
        # Create an expired token manually (this is a simplified test)
        # In practice, we'd need to manipulate JWT expiration
        # For now, we test with an invalid token format
        response = test_client.post(
            "/api/v1/auth/refresh",
            json={"refresh_token": "expired.token.here"}
        )
        
        assert response.status_code == 401

    def test_refresh_token_missing_token(self, test_client: TestClient):
        """Verify 422 when refresh_token is missing."""
        response = test_client.post(
            "/api/v1/auth/refresh",
            json={}
        )
        
        assert response.status_code == 422


class TestCurrentUser:
    """Test current user info endpoint."""

    def test_get_current_user_info_authenticated(
        self, test_client: TestClient, auth_headers: dict, admin_user
    ):
        """Verify user info retrieval with valid token."""
        response = test_client.get(
            "/api/v1/auth/me",
            headers=auth_headers
        )
        
        assert response.status_code == 200
        data = response.json()
        assert data["username"] == "testadmin"
        assert data["is_admin"] is True
        assert "role" in data
        assert "permissions" in data

    def test_get_current_user_info_unauthenticated(
        self, test_client: TestClient
    ):
        """Verify 401 without token."""
        response = test_client.get("/api/v1/auth/me")
        
        assert response.status_code == 401

    def test_get_current_user_info_bootstrap_admin(
        self, test_client: TestClient, test_db_session: Session
    ):
        """Verify admin bootstrap when no admins exist."""
        # Ensure no admin users exist
        from backend.db.models import User
        test_db_session.query(User).filter(User.is_admin == True).delete()
        test_db_session.commit()
        
        # Login with hardcoded admin
        login_response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "admin", "password": "admin"}
        )
        token = login_response.json()["access_token"]
        headers = {"Authorization": f"Bearer {token}"}
        
        # Get user info - should bootstrap as admin
        response = test_client.get(
            "/api/v1/auth/me",
            headers=headers
        )
        
        assert response.status_code == 200
        data = response.json()
        assert data["username"] == "admin"
        assert data["is_admin"] is True

    def test_get_current_user_info_role_permissions(
        self, test_client: TestClient, auth_headers: dict, admin_user
    ):
        """Verify role and permissions are returned."""
        response = test_client.get(
            "/api/v1/auth/me",
            headers=auth_headers
        )
        
        assert response.status_code == 200
        data = response.json()
        assert "role" in data
        assert "permissions" in data
        assert isinstance(data["permissions"], dict)


class TestPasswordChange:
    """Test password change endpoint."""

    def test_change_password_success(
        self, test_client: TestClient, auth_headers: dict, admin_user
    ):
        """Verify successful password change."""
        response = test_client.post(
            "/api/v1/auth/change-password",
            headers=auth_headers,
            json={
                "current_password": "testpass",
                "new_password": "newtestpass123"
            }
        )
        
        assert response.status_code == 200
        data = response.json()
        assert data["success"] is True
        assert "Password changed successfully" in data["message"]
        
        # Verify new password works
        login_response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "testadmin", "password": "newtestpass123"}
        )
        assert login_response.status_code == 200

    def test_change_password_with_wrong_current_password(
        self, test_client: TestClient, auth_headers: dict, admin_user
    ):
        """Verify 401 with incorrect current password."""
        response = test_client.post(
            "/api/v1/auth/change-password",
            headers=auth_headers,
            json={
                "current_password": "wrongpassword",
                "new_password": "newtestpass123"
            }
        )
        
        assert response.status_code == 401
        data = response.json()
        assert "detail" in data
        assert "Current password is incorrect" in data["detail"]

    def test_change_password_clears_password_change_required_flag(
        self, test_client: TestClient, test_db_session: Session
    ):
        """Verify flag is cleared after password change."""
        from backend.db.models import User
        from backend.utils.password import hash_password
        from backend.constants.roles import DEFAULT_USER_ROLE
        
        # Create user with password_change_required flag
        user = User(
            username="changepassuser",
            email="changepass@example.com",
            password_hash=hash_password("oldpass"),
            full_name="Change Password User",
            is_admin=False,
            is_active=True,
            password_change_required=True,
            role=DEFAULT_USER_ROLE,
        )
        test_db_session.add(user)
        test_db_session.commit()
        
        # Login
        login_response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "changepassuser", "password": "oldpass"}
        )
        token = login_response.json()["access_token"]
        headers = {"Authorization": f"Bearer {token}"}
        
        # Change password
        response = test_client.post(
            "/api/v1/auth/change-password",
            headers=headers,
            json={
                "current_password": "oldpass",
                "new_password": "newpass123"
            }
        )
        
        assert response.status_code == 200
        
        # Verify flag is cleared
        test_db_session.refresh(user)
        assert user.password_change_required is False

    def test_change_password_user_not_found(
        self, test_client: TestClient, test_db_session: Session
    ):
        """Verify 404 when user doesn't exist in database."""
        # Create a token for a user that doesn't exist in main DB
        # This is a bit tricky - we'll use the hardcoded admin
        login_response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "admin", "password": "admin"}
        )
        token = login_response.json()["access_token"]
        headers = {"Authorization": f"Bearer {token}"}
        
        # Try to change password - should work for hardcoded admin
        # But if we delete the user from DB, it should fail
        from backend.db.models import User
        user = test_db_session.query(User).filter(User.username == "admin").first()
        if user:
            test_db_session.delete(user)
            test_db_session.commit()
        
        # Now try to change password
        response = test_client.post(
            "/api/v1/auth/change-password",
            headers=headers,
            json={
                "current_password": "admin",
                "new_password": "newpass123"
            }
        )
        
        # Should fail because user not in database
        assert response.status_code == 404
        data = response.json()
        assert "User not found" in data["detail"]

    def test_change_password_missing_fields(self, test_client: TestClient, auth_headers: dict):
        """Verify 422 when required fields are missing."""
        # Missing current_password
        response = test_client.post(
            "/api/v1/auth/change-password",
            headers=auth_headers,
            json={"new_password": "newpass123"}
        )
        assert response.status_code == 422
        
        # Missing new_password
        response = test_client.post(
            "/api/v1/auth/change-password",
            headers=auth_headers,
            json={"current_password": "testpass"}
        )
        assert response.status_code == 422


class TestAuthenticationMiddleware:
    """Test authentication middleware and token validation."""

    def test_get_current_user_without_token(self, test_client: TestClient):
        """Verify 401 without Authorization header."""
        # Try to access protected endpoint
        response = test_client.get("/api/v1/photos")
        
        assert response.status_code == 401
        data = response.json()
        assert "detail" in data

    def test_get_current_user_with_expired_token(self, test_client: TestClient):
        """Verify 401 with expired JWT."""
        # Create an obviously invalid/expired token
        # Note: This is a test fixture, not a real secret. The token is intentionally invalid.
        expired_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTYwOTQ1NjgwMH0.invalid"
        
        response = test_client.get(
            "/api/v1/photos",
            headers={"Authorization": f"Bearer {expired_token}"}
        )
        
        assert response.status_code == 401

    def test_get_current_user_with_invalid_token_format(
        self, test_client: TestClient
    ):
        """Verify 401 with malformed token."""
        response = test_client.get(
            "/api/v1/photos",
            headers={"Authorization": "Bearer not.a.valid.jwt.token"}
        )
        
        assert response.status_code == 401

    def test_get_current_user_with_id_creates_user(
        self, test_client: TestClient, test_db_session: Session
    ):
        """Verify user creation in bootstrap scenario."""
        from backend.db.models import User
        
        # Delete user if exists
        test_db_session.query(User).filter(User.username == "bootstrapuser").delete()
        test_db_session.commit()
        
        # Login with hardcoded admin to get token
        login_response = test_client.post(
            "/api/v1/auth/login",
            json={"username": "admin", "password": "admin"}
        )
        token = login_response.json()["access_token"]
        headers = {"Authorization": f"Bearer {token}"}
        
        # Access endpoint that uses get_current_user_with_id
        # This should create the user in the database
        # Note: This depends on which endpoints use get_current_user_with_id
        # For now, we'll verify the user can be created via /auth/me
        response = test_client.get(
            "/api/v1/auth/me",
            headers=headers
        )
        
        assert response.status_code == 200
        
        # Verify user exists in database (if bootstrap happened)
        # This is a simplified test - actual bootstrap logic may vary

