"""Tests for the initial admin bootstrap service."""

import uuid
from unittest.mock import patch

import pytest
import pytest_asyncio
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from src.config.settings import Settings
from src.models.organisation import Organisation
from src.models.user import User
from src.services.auth import verify_password
from src.services.bootstrap import bootstrap_initial_admin
from tests.conftest import requires_db


def _settings(**overrides) -> Settings:
    base: dict = dict(
        environment="test",
        initial_admin_email=None,
        initial_admin_password=None,
        initial_admin_full_name="Administrator",
        initial_org_name="Default Organisation",
        initial_org_slug="default",
    )
    base.update(overrides)
    return Settings(**base)


class TestBootstrapNoOp:
    """Pure unit tests — bootstrap must short-circuit before touching the DB."""

    async def test_noop_when_email_unset(self):
        settings = _settings(initial_admin_password="pw")
        with patch("src.services.bootstrap.async_session_factory") as factory:
            await bootstrap_initial_admin(settings)
        factory.assert_not_called()

    async def test_noop_when_password_unset(self):
        settings = _settings(initial_admin_email="admin@example.com")
        with patch("src.services.bootstrap.async_session_factory") as factory:
            await bootstrap_initial_admin(settings)
        factory.assert_not_called()


@requires_db
class TestBootstrapWithDatabase:
    """Integration tests — exercise the real SQL path."""

    @pytest_asyncio.fixture(loop_scope="session")
    async def clean_db(self, _test_engine, _setup_db):
        """Strip users and orgs so bootstrap sees an empty table."""
        async with AsyncSession(_test_engine, expire_on_commit=False) as session:
            await session.execute(User.__table__.delete())
            await session.execute(Organisation.__table__.delete())
            await session.commit()
        yield
        async with AsyncSession(_test_engine, expire_on_commit=False) as session:
            await session.execute(User.__table__.delete())
            await session.execute(Organisation.__table__.delete())
            await session.commit()

    async def test_creates_org_and_owner_when_empty(self, _test_engine, clean_db):
        email = f"admin-{uuid.uuid4().hex[:8]}@example.com"
        slug = f"bootstrap-{uuid.uuid4().hex[:8]}"
        settings = _settings(
            initial_admin_email=email,
            initial_admin_password="SuperSecret123",
            initial_org_slug=slug,
            initial_org_name="Bootstrapped Org",
        )

        def _factory():
            return AsyncSession(_test_engine, expire_on_commit=False)

        with patch("src.services.bootstrap.async_session_factory", _factory):
            await bootstrap_initial_admin(settings)

        async with AsyncSession(_test_engine, expire_on_commit=False) as session:
            user = (await session.execute(select(User).where(User.email == email))).scalar_one()
            org = (
                await session.execute(select(Organisation).where(Organisation.slug == slug))
            ).scalar_one()

        assert user.role == "owner"
        assert user.organisation_id == org.id
        assert user.full_name == "Administrator"
        assert verify_password("SuperSecret123", user.password_hash)
        assert org.name == "Bootstrapped Org"
        assert org.contact_email == email

    async def test_idempotent_when_user_exists(self, _test_engine, clean_db):
        """A second invocation must not create a second user."""
        email = f"admin-{uuid.uuid4().hex[:8]}@example.com"
        slug = f"bootstrap-{uuid.uuid4().hex[:8]}"
        settings = _settings(
            initial_admin_email=email,
            initial_admin_password="SuperSecret123",
            initial_org_slug=slug,
        )

        def _factory():
            return AsyncSession(_test_engine, expire_on_commit=False)

        with patch("src.services.bootstrap.async_session_factory", _factory):
            await bootstrap_initial_admin(settings)
            await bootstrap_initial_admin(
                _settings(
                    initial_admin_email="someone-else@example.com",
                    initial_admin_password="Different123",
                    initial_org_slug=slug,
                )
            )

        async with AsyncSession(_test_engine, expire_on_commit=False) as session:
            users = (await session.execute(select(User))).scalars().all()

        assert len(users) == 1
        assert users[0].email == email


pytestmark = pytest.mark.asyncio(loop_scope="session")
