"""User authentication primitives.

Cassandra is **passwordless**. Every login is an email-OTP round-trip
(see app.services.otp_service + app.services.email_service). This module
just handles user-row lookup and create-on-first-sight.

The trade-off (see Phase G plan in tasks/todo.md):
- Server holds: email, tier, AI cost ledger. No portfolio, no broker keys.
- Loss of password gives up nothing of value to protect; gains: no
  password-reset flows, no hash rotation, no stuffing/breach exposure.
- Every successful session is by construction proof of email control.
"""
from __future__ import annotations

from email_validator import EmailNotValidError, validate_email
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.db import utcnow
from app.models import User


class AuthError(Exception):
    """Raised on bad input. The message is safe to surface to the user."""


def _validate_email_or_raise(email: str) -> str:
    try:
        info = validate_email(email, check_deliverability=False)
        return info.normalized.lower()
    except EmailNotValidError as e:
        raise AuthError(f"Invalid email: {e}")


async def get_user(session: AsyncSession, user_id: int) -> User | None:
    return (await session.execute(
        select(User).where(User.id == user_id)
    )).scalar_one_or_none()


async def get_user_by_email(session: AsyncSession, email: str) -> User | None:
    email = email.strip().lower()
    return (await session.execute(
        select(User).where(User.email == email)
    )).scalar_one_or_none()


async def get_or_create_user(
    session: AsyncSession,
    email: str,
    *,
    create_if_missing: bool = True,
    tier: str = "free",
) -> User:
    """Look up the user by email; create if absent and create_if_missing.
    Raises AuthError on malformed email, or if create_if_missing=False
    and the email is unknown.

    Callers should set create_if_missing=False when CASSANDRA_SIGNUP_ENABLED
    is false — i.e., the operator is running a closed deployment."""
    email = _validate_email_or_raise(email)
    user = await get_user_by_email(session, email)
    if user is not None:
        return user
    if not create_if_missing:
        raise AuthError("Sign-ups are currently disabled. Ask the operator.")
    user = User(email=email, tier=tier, settings_json={}, created_at=utcnow())
    session.add(user)
    await session.commit()
    await session.refresh(user)
    return user
