"""
Auth policy integration: server-derived admin vs viewer, ignored client role, UMC domain.

Requires Postgres + migration 007 (admin_from_email_policy column) and 013 (email_verified).

  export INITIATIVE_DATABASE_URL="postgresql+asyncpg://initiative:initiative_secret@127.0.0.1:15432/initiatives"
  cd be0 && python -m unittest tests.test_auth_policy_integration -v
"""

from __future__ import annotations

import os
import unittest
import uuid
from unittest.mock import patch

from sqlalchemy import select

from tests.auth_register_staff_fixture import register_staff_fields

_RUN_DB = os.getenv("INITIATIVE_DATABASE_URL", "").strip().lower().startswith("postgresql")

_TEST_PASSWORD = "Testpass1!"


@unittest.skipUnless(
    _RUN_DB,
    "Set INITIATIVE_DATABASE_URL=postgresql+asyncpg://.../initiatives to run DB integration tests",
)
class AuthPolicyIntegrationTests(unittest.IsolatedAsyncioTestCase):
    async def asyncSetUp(self) -> None:
        from src.initiative_db import engine as eng

        await eng.dispose_engine()
        await eng.init_engine()

    async def asyncTearDown(self) -> None:
        from src.initiative_db import engine as eng

        await eng.dispose_engine()

    async def _delete_user_email(self, email: str) -> None:
        from src.initiative_db.engine import get_session
        from src.initiative_db.models import User

        async with get_session() as session:
            user = (
                await session.execute(select(User).where(User.email == email))
            ).scalar_one_or_none()
            if user is not None:
                await session.delete(user)

    def _register(self, email: str, full_name: str = "Policy Test", extra: dict | None = None):
        from fastapi.testclient import TestClient

        from main import app

        body = {
            "fullName": full_name,
            "email": email,
            "password": _TEST_PASSWORD,
            "passwordConfirm": _TEST_PASSWORD,
            **register_staff_fields(),
        }
        if extra:
            body.update(extra)
        with TestClient(app) as client:
            return client.post("/api/v1/auth/register", json=body)

    async def test_register_default_viewer_ump(self) -> None:
        email = f"applicant-{uuid.uuid4().hex[:12]}@ump.edu.vn"
        try:
            r = self._register(email)
            self.assertEqual(r.status_code, 200, r.text)
            data = r.json()
            self.assertTrue(data.get("emailVerificationRequired"))
            self.assertNotIn("accessToken", data)
            roles = data["user"]["roles"]
            self.assertIn("viewer", roles)
            self.assertNotIn("admin", roles)
        finally:
            await self._delete_user_email(email)

    async def test_register_default_viewer_umc(self) -> None:
        email = f"applicant-{uuid.uuid4().hex[:12]}@umc.edu.vn"
        try:
            r = self._register(email)
            self.assertEqual(r.status_code, 200, r.text)
            self.assertTrue(r.json().get("emailVerificationRequired"))
            roles = r.json()["user"]["roles"]
            self.assertIn("viewer", roles)
            self.assertNotIn("admin", roles)
        finally:
            await self._delete_user_email(email)

    async def test_register_ignores_client_admin_role(self) -> None:
        email = f"privesc-{uuid.uuid4().hex[:12]}@ump.edu.vn"
        try:
            r = self._register(
                email,
                extra={"role": "admin"},
            )
            self.assertEqual(r.status_code, 200, r.text)
            self.assertTrue(r.json().get("emailVerificationRequired"))
            roles = r.json()["user"]["roles"]
            self.assertIn("viewer", roles)
            self.assertNotIn("admin", roles)
        finally:
            await self._delete_user_email(email)

    async def test_policy_env_makes_admin(self) -> None:
        email = f"stub-admin-{uuid.uuid4().hex[:12]}@ump.edu.vn"
        try:
            with patch.dict(os.environ, {"AUTH_ADMIN_EMAILS": email}):
                r = self._register(email)
            self.assertEqual(r.status_code, 200, r.text)
            self.assertTrue(r.json().get("emailVerificationRequired"))
            roles = r.json()["user"]["roles"]
            self.assertIn("admin", roles)
            self.assertNotIn("viewer", roles)
        finally:
            await self._delete_user_email(email)


if __name__ == "__main__":
    unittest.main()
