"""Effective capability = requested (prompt-inferred, persisted) | required
(composition-structural). staff_admin_shell is seeded for every html/react_app/
nuxt_app project and only implements magic-link sign-in, so those project types
structurally require auth.magic_link whether or not a prompt ever said so --
see runtime_contract.py's _required_auth_modes and resolve_runtime_contract."""
from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import AsyncMock
from uuid import uuid4

import pytest
from src.domain.codegen.runtime_contract import _required_auth_modes, resolve_runtime_contract


@pytest.mark.parametrize("project_type", ["html", "react_app", "nuxt_app"])
def test_page_project_types_structurally_require_magic_link(project_type):
    assert _required_auth_modes(project_type) == {"magic_link"}


def test_unrecognized_project_type_requires_nothing():
    assert _required_auth_modes("unknown_future_type") == set()


def _project(project_type="html", auth_modes=None):
    return SimpleNamespace(
        id=uuid4(),
        workspace_id=uuid4(),
        type=project_type,
        runtime_invariants={"auth_modes": auth_modes or []},
    )


@pytest.mark.asyncio
async def test_runtime_contract_accepts_dbless_fixture_and_missing_optional_name(monkeypatch):
    """The resolver is also used by dry-run/golden-runtime probes without a DB.

    Missing optional fixture fields must not turn an auth contract probe into an
    exception or cause it to issue a database call through ``None``.
    """
    _wire_email_unavailable(monkeypatch)
    contract = await resolve_runtime_contract(db=None, project=_project("html"))
    assert contract.project == {"name": "", "type": "html"}
    assert contract.capabilities["auth"]["magic_link"] is True


def _wire_email_unavailable(monkeypatch):
    """Keep the email side of the contract deterministic (unavailable) so these
    tests are only exercising the auth_modes union, not the email resolver."""
    monkeypatch.setattr(
        "src.domain.projects.repository.ProjectSettingsRepository",
        lambda db: SimpleNamespace(
            get_or_create=AsyncMock(return_value=SimpleNamespace(settings={})),
            get_search_index_adapters=AsyncMock(return_value=[]),
        ),
    )
    monkeypatch.setattr(
        "src.domain.connectors.service.ConnectorService",
        lambda repo, cloud_client: SimpleNamespace(resolve_email_send=AsyncMock(return_value=None)),
    )
    monkeypatch.setattr("src.domain.connectors.repository.ConnectorRepository", lambda db: None)
    monkeypatch.setattr("src.integrations.cloud_control_client.CloudControlClient", lambda: None)
    monkeypatch.setattr(
        "src.domain.email_domains.repository.EmailDomainRepository",
        lambda db: SimpleNamespace(get_by_workspace=AsyncMock(return_value=None)),
    )
    monkeypatch.setattr(
        "src.integrations.platform_ports.enabled_capabilities", lambda settings: set()
    )
    monkeypatch.setattr(
        "src.integrations.email_provider.cloud_email_transport_configured", lambda settings: False
    )


@pytest.mark.asyncio
async def test_html_project_gets_magic_link_even_with_no_prompt_signal(monkeypatch):
    import structlog.testing

    _wire_email_unavailable(monkeypatch)
    project = _project("html", auth_modes=[])
    with structlog.testing.capture_logs() as logs:
        contract = await resolve_runtime_contract(db=None, project=project)
    assert contract.capabilities["auth"]["magic_link"] is True
    [event] = [log for log in logs if log["event"] == "capability.auth.required_by_composition"]
    assert event["project_id"] == str(project.id)
    assert event["auth_modes"] == ["magic_link"]


@pytest.mark.asyncio
async def test_no_composition_log_when_the_prompt_already_requested_it(monkeypatch):
    import structlog.testing

    _wire_email_unavailable(monkeypatch)
    with structlog.testing.capture_logs() as logs:
        await resolve_runtime_contract(db=None, project=_project("html", auth_modes=["magic_link"]))
    assert not [log for log in logs if log["event"] == "capability.auth.required_by_composition"]


@pytest.mark.asyncio
async def test_prompt_requested_magic_link_is_still_honored(monkeypatch):
    """Requested and required are a union, not a replacement -- a prompt that
    explicitly asked for magic link keeps working even for a hypothetical
    future project type that doesn't structurally require it."""
    _wire_email_unavailable(monkeypatch)
    contract = await resolve_runtime_contract(
        db=None, project=_project("some_other_type", auth_modes=["magic_link"])
    )
    assert contract.capabilities["auth"]["magic_link"] is True


@pytest.mark.asyncio
async def test_non_page_project_type_without_a_request_does_not_get_magic_link(monkeypatch):
    _wire_email_unavailable(monkeypatch)
    contract = await resolve_runtime_contract(
        db=None, project=_project("some_other_type", auth_modes=[])
    )
    assert contract.capabilities["auth"]["magic_link"] is False
