import pytest
from unittest.mock import patch, MagicMock
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
import uuid

from fastapi import FastAPI
from preloop.api.auth.router import router as auth_router
from preloop.models.models.user import User

app = FastAPI()
app.include_router(auth_router, prefix="/auth")
client = TestClient(app)


@pytest.fixture
def db_session_mock():
    from preloop.models.db.session import get_db_session

    db_session = MagicMock(spec=Session)
    mock_execute = MagicMock()
    mock_scalars = MagicMock()
    mock_scalars.first.return_value = None
    mock_execute.scalars.return_value = mock_scalars
    db_session.execute.return_value = mock_execute

    # Mock the query chain for CRUD methods
    mock_query = MagicMock()
    mock_query.filter.return_value = mock_query
    mock_query.first.return_value = None
    mock_query.all.return_value = []
    db_session.query.return_value = mock_query

    app.dependency_overrides[get_db_session] = lambda: db_session
    try:
        yield db_session
    finally:
        app.dependency_overrides.pop(get_db_session, None)


def test_register_user_success(db_session_mock):
    with patch("preloop.api.auth.router.complete_new_account_setup_background"):
        response = client.post(
            "/auth/register",
            json={
                "username": "testuser",
                "email": "test@example.com",
                "password": "password",
                "full_name": "Test User",
            },
        )
        assert response.status_code == 201
        data = response.json()
        assert data["username"] == "testuser"
        assert data["email"] == "test@example.com"
        assert data["full_name"] == "Test User"
        assert not data["email_verified"]


def test_register_user_username_exists(db_session_mock):
    mock_user = MagicMock(spec=User)
    mock_user.username = "testuser"
    mock_user.email = "test@example.com"

    with patch("preloop.api.auth.router.crud_user") as mock_crud:
        mock_crud.get_by_username.return_value = mock_user

        response = client.post(
            "/auth/register",
            json={
                "username": "testuser",
                "email": "another@example.com",
                "password": "password",
                "full_name": "Test User",
            },
        )
        assert response.status_code == 400
        assert response.json()["detail"] == "Username already registered"


def test_register_user_email_exists(db_session_mock):
    mock_user = MagicMock(spec=User)
    mock_user.username = "anotheruser"
    mock_user.email = "test@example.com"

    with patch("preloop.api.auth.router.crud_user") as mock_crud:
        mock_crud.get_by_username.return_value = None
        mock_crud.get_by_email.return_value = mock_user

        response = client.post(
            "/auth/register",
            json={
                "username": "testuser",
                "email": "test@example.com",
                "password": "password",
                "full_name": "Test User",
            },
        )
        assert response.status_code == 400
        assert response.json()["detail"] == "Email already registered"


def test_register_disabled(db_session_mock):
    """Test that registration is blocked when REGISTRATION_ENABLED is false."""
    with patch("preloop.api.auth.router.settings") as mock_settings:
        # Disable registration
        mock_settings.registration_enabled = False

        response = client.post(
            "/auth/register",
            json={
                "username": "testuser",
                "email": "test@example.com",
                "password": "password",
                "full_name": "Test User",
            },
        )

        # Should return 403 Forbidden
        assert response.status_code == 403
        assert "Registration is disabled" in response.json()["detail"]

        # Verify that no database operations were performed
        # (the endpoint should reject before checking username/email)
        db_session_mock.query.assert_not_called()


def test_login_success():
    with patch("preloop.api.auth.router.authenticate_user") as mock_authenticate_user:
        mock_user = MagicMock(spec=User)
        mock_user.id = uuid.uuid4()
        mock_user.username = "testuser"
        mock_user.email = "test@example.com"
        mock_user.hashed_password = "hashed_password"
        mock_authenticate_user.return_value = mock_user

        response = client.post(
            "/auth/token/json",
            json={"username": "testuser", "password": "password"},
        )
        assert response.status_code == 200
        data = response.json()
        assert "access_token" in data
        assert "refresh_token" in data
        assert data["token_type"] == "bearer"


def test_login_failure():
    with patch("preloop.api.auth.router.authenticate_user") as mock_authenticate_user:
        mock_authenticate_user.return_value = None

        response = client.post(
            "/auth/token/json",
            json={"username": "wronguser", "password": "password"},
        )
        assert response.status_code == 401
        assert response.json()["detail"] == "Incorrect username or password"
