"""Dev Mode and the outbox.

Manager-side only: whether the app mails clients is an agency decision, and the
record of what it decided to send is an agency artefact.
"""

import logging

from fastapi import APIRouter, Body, Depends, HTTPException
from sqlalchemy import select

from app import schemas
from app import bw_accounts as bwa
from app.db import get_session_factory
from app.identity import optional_user
from app.models import OutboundMessage
from app.services import digest, outbound

log = logging.getLogger(__name__)
router = APIRouter(prefix="/api/outbound", tags=["outbound"])


def _manager(user: str | None = Depends(optional_user)) -> str:
    assert user is not None
    # Anyone who manages a project is agency-side; the setting is app-wide.
    if not bwa.can(user, "projects.manage") and not any(
            bwa.can(user, "projects.manage", i["id"]) for i in bwa.instances()):
        raise HTTPException(status_code=403, detail={
            "error_code": "FORBIDDEN",
            "summary": "That is a team setting."})
    return user


@router.get("/settings", response_model=schemas.SettingsOut)
def get_settings_(user: str = Depends(_manager)) -> schemas.SettingsOut:
    sf = get_session_factory()
    with sf() as db:
        return schemas.SettingsOut(dev_mode=outbound.dev_mode(db),
                                   held=outbound.held_count(db))


@router.post("/settings", response_model=schemas.SettingsOut)
def set_settings(body: schemas.SetDevModeIn,
                 user: str = Depends(_manager)) -> schemas.SettingsOut:
    """Turn client email off while testing, and on when it is real.

    Default is ON (suppressed): the safe default for a switch whose off position
    mails clients is the one that does not.
    """
    sf = get_session_factory()
    with sf() as db:
        outbound.set_dev_mode(db, body.dev_mode)
        db.commit()
        log.info("dev_mode set to %s by %s", body.dev_mode, user)
        return schemas.SettingsOut(dev_mode=outbound.dev_mode(db),
                                   held=outbound.held_count(db))


@router.post("/digest", response_model=schemas.DigestRunOut)
def run_digest(body: schemas.DigestRunIn = Body(default=None),
               user: str = Depends(_manager)) -> schemas.DigestRunOut:
    """What the background loop does every few minutes, on demand: retry
    held mail, then send digests. `quiet_minutes: 0` mails everyone with
    something waiting right now, for testing."""
    sf = get_session_factory()
    with sf() as db:
        retried = outbound.retry_held(db)
        queued = digest.run_once(
            db, quiet_minutes=body.quiet_minutes if body else None)
        db.commit()
        log.info("digest run by %s: %d queued, %d retried", user, queued, retried)
    return schemas.DigestRunOut(queued=queued, retried=retried)


@router.get("", response_model=schemas.OutboxOut)
def outbox(limit: int = 50, user: str = Depends(_manager)) -> schemas.OutboxOut:
    """What easel decided to send, and whether it went.

    This is how the email design is checked before a transport exists: read what
    would have reached whom, and fix the wording, with nothing leaving the
    building.
    """
    sf = get_session_factory()
    with sf() as db:
        rows = db.scalars(
            select(OutboundMessage)
            .order_by(OutboundMessage.id.desc())
            .limit(min(max(limit, 1), 200))).all()
        return schemas.OutboxOut(messages=[
            schemas.OutboundOut(
                id=m.id, recipient=m.recipient, to_email=m.to_email,
                kind=m.kind, subject=m.subject, body=m.body, url=m.url,
                project_id=m.project_id, created_at=m.created_at,
                sent_at=m.sent_at, held_reason=m.held_reason)
            for m in rows])
