# #region TestCreateAdmin [C:2] [TYPE Module] [SEMANTICS test,admin,user,cli]
# @BRIEF Tests for create_admin.py script (mocked DB, no real auth.db).
# @RELATION BINDS_TO -> [CreateAdminScript]
# @TEST_EDGE: create_admin_new_user -> Creates user and role successfully
# @TEST_EDGE: create_admin_user_exists -> Returns "exists"
# @TEST_EDGE: create_admin_existing_role -> Reuses existing Admin role
# @TEST_EDGE: create_admin_email_normalized -> Strips whitespace from email
# @TEST_EDGE: create_admin_db_error -> Raises on DB failure
# @TEST_EDGE: create_admin_no_email -> Works with email=None
# @TEST_EDGE: create_admin_empty_email -> Normalizes empty string to None
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch, PropertyMock

import pytest

sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))


# =============================================================================
# create_admin
# =============================================================================

class TestCreateAdmin:
    """Tests for create_admin.create_admin()."""

    @patch("src.scripts.create_admin.AuthSessionLocal")
    @patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
    def test_creates_new_user(self, mock_hash, MockSession):
        """Happy: creates admin user and role."""
        from src.scripts.create_admin import create_admin

        # Mock DB session
        mock_db = MagicMock()
        MockSession.return_value = mock_db

        # No existing Admin role, no existing user
        mock_db.query.return_value.filter.return_value.first.side_effect = [
            None,  # Role query -> no role
            None,  # User query -> no user
        ]

        result = create_admin("admin", "secret123", "admin@test.com")

        assert result == "created"
        # add() was called at least once (for role and/or user)
        assert mock_db.add.called
        assert mock_db.commit.call_count >= 1
        mock_db.close.assert_called_once()

    @patch("src.scripts.create_admin.AuthSessionLocal")
    @patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
    def test_user_already_exists(self, mock_hash, MockSession):
        """Edge: existing user returns 'exists'."""
        from src.scripts.create_admin import create_admin

        mock_db = MagicMock()
        MockSession.return_value = mock_db

        # Existing Admin role
        existing_role = MagicMock()
        existing_role.name = "Admin"
        # Existing user
        existing_user = MagicMock()
        existing_user.username = "admin"

        def query_side_effect(model):
            mock_query = MagicMock()
            if model.__name__ == "Role":
                mock_query.filter.return_value.first.return_value = existing_role
            elif model.__name__ == "User":
                mock_query.filter.return_value.first.return_value = existing_user
            return mock_query

        mock_db.query.side_effect = query_side_effect

        result = create_admin("admin", "secret123")

        assert result == "exists"
        mock_db.close.assert_called_once()

    @patch("src.scripts.create_admin.AuthSessionLocal")
    @patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
    def test_email_normalized_strip(self, mock_hash, MockSession):
        """Edge: email with whitespace is normalized."""
        from src.scripts.create_admin import create_admin

        mock_db = MagicMock()
        MockSession.return_value = mock_db

        existing_role = MagicMock()
        existing_role.name = "Admin"
        mock_db.query.return_value.filter.return_value.first.side_effect = [
            existing_role,  # Role exists
            None,  # No existing user
        ]

        result = create_admin("admin2", "secret123", "  admin@test.com  ")

        assert result == "created"
        # Check the user was created with stripped email
        added_user = [a for a in mock_db.add.call_args_list if "User" in str(a)]
        assert added_user  # User was added

    @patch("src.scripts.create_admin.AuthSessionLocal")
    @patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
    def test_empty_email_normalized_to_none(self, mock_hash, MockSession):
        """Edge: empty email string is normalized to None."""
        from src.scripts.create_admin import create_admin

        mock_db = MagicMock()
        MockSession.return_value = mock_db

        existing_role = MagicMock()
        existing_role.name = "Admin"
        mock_db.query.return_value.filter.return_value.first.side_effect = [
            existing_role,  # Role exists
            None,  # No existing user
        ]

        result = create_admin("admin3", "secret123", "   ")

        assert result == "created"

    @patch("src.scripts.create_admin.AuthSessionLocal")
    @patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
    def test_no_email(self, mock_hash, MockSession):
        """Edge: email=None works without error."""
        from src.scripts.create_admin import create_admin

        mock_db = MagicMock()
        MockSession.return_value = mock_db

        existing_role = MagicMock()
        existing_role.name = "Admin"
        mock_db.query.return_value.filter.return_value.first.side_effect = [
            existing_role,
            None,
        ]

        result = create_admin("admin4", "secret123", None)

        assert result == "created"

    @patch("src.scripts.create_admin.AuthSessionLocal")
    @patch("src.scripts.create_admin.get_password_hash", return_value="hashed_pwd")
    def test_db_error_rollback_and_raise(self, mock_hash, MockSession):
        """Edge: DB error triggers rollback and re-raises."""
        from src.scripts.create_admin import create_admin

        mock_db = MagicMock()
        MockSession.return_value = mock_db

        # No existing role + no existing user -> will hit commit when creating user
        mock_db.query.return_value.filter.return_value.first.side_effect = [
            None,  # Role query -> no role
            None,  # User query -> no user
        ]

        # First commit (for role) succeeds, second commit (for user) fails
        mock_db.commit.side_effect = [None, Exception("DB failure")]

        with pytest.raises(Exception, match="DB failure"):
            create_admin("admin", "secret123")

        mock_db.rollback.assert_called_once()
        mock_db.close.assert_called_once()
# #endregion TestCreateAdmin
