"""Per-project projection eligibility (rollout blocker, design §15).

The environment flag is necessary but never sufficient: a project must also be explicitly
enabled (or shadowed) in its own auth config before Core reconciles it or Cloud receives a write.
"""

from __future__ import annotations

from uuid import UUID, uuid4

from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession

from src.domain.project_auth.audit import record_event
from src.domain.project_auth.models import ProjectAuthConfig

DISABLED = "disabled"
SHADOW = "shadow"
ENABLED = "enabled"


def may_reconcile(system_enabled: bool, state: str) -> bool:
    return system_enabled and state in (SHADOW, ENABLED)


def may_write_cloud(system_enabled: bool, state: str) -> bool:
    return system_enabled and state == ENABLED


async def project_state(db: AsyncSession, project_id: UUID) -> str:
    state = await db.scalar(
        select(ProjectAuthConfig.projection_state).where(ProjectAuthConfig.project_id == project_id)
    )
    return state or DISABLED


async def set_projection_state(db: AsyncSession, project_id: UUID, state: str) -> tuple[str, str]:
    """Set one project's projection state and audit the change. Caller commits.

    Returns (previous, new). Raises ValueError for an unknown state or a missing config row.
    The caller decides which states are allowed for this run.
    """
    if state not in (DISABLED, SHADOW, ENABLED):
        raise ValueError(f"unknown_projection_state:{state}")
    previous = await project_state(db, project_id)
    if previous == state:
        return previous, state
    result = await db.execute(
        update(ProjectAuthConfig)
        .where(ProjectAuthConfig.project_id == project_id)
        .values(projection_state=state)
    )
    if result.rowcount != 1:
        raise ValueError("project_auth_config_missing")
    await record_event(
        db,
        project_id=project_id,
        event="projection_state_changed",
        actor_membership_id=None,
        target_membership_id=None,
        payload={"from": previous, "to": state, "actor": "backfill_script", "run": str(uuid4())},
        correlation_id=None,
    )
    return previous, state
