agilentics / boiler
"""Self-service sign-up: turning a stranger into an organisation with an owner.

Every other way an account comes into being here is a *provisioning* - the CLI,
an operator adding a colleague, a test fixture - where somebody already inside
decided it should exist. This is the one path where the person creating the
account is a stranger, and that difference is the whole reason the module exists
separately from `orgs.provision`.

What this makes is a request to be let in, not an admission. The owner is created
with both gates shut, and the tenant it builds sits idle until each opens:

    the emailed link      stamps email_verified_at   - the address is theirs
    an operator           stamps approved_at         - the account should exist

Confirming an address does not admit anybody on its own. Minting and mailing the
verification token is the façade's job (auth.request_email_verification, then
mail.send_email_verification), kept out of here so the core stays transport-free.
"""
import logging

from db.db_declarations import Organization, User

from . import auth as auth_core
from . import orgs as orgs_core
from .context import Invalid

logger = logging.getLogger(__name__)


def register_org_and_owner(session, email: str, password: str,
                           org_name: str | None = None) -> tuple[User, Organization]:
    """Create an organisation and its owner in one go.

    The user is created first on purpose: `create_user` enforces the email and
    password rules and rejects a duplicate address, so a sign-up that is going to
    fail leaves no half-built organisation behind. `provision` cannot fail on a
    missing name - it falls back to the local part of the address - so there is
    no second failure to unwind.

    Raises Invalid if the address already has an account. A person signing up
    with one should sign in, or recover, instead.
    """
    user = auth_core.create_user(session, email, password, name=None,
                                 verified=False, approved=False)
    org = orgs_core.provision(session, user, org_name)
    logger.info("Registered %s as owner of new org %r.", user.email, org.slug)
    return user, org