"""The one public write for email capture: POST /api/subscribers.

Sources of truth: `services/subscribers.py` (the rules), `services/throttle.py` (the limit),
`models/editorial.py` (the shapes). Listed in tests/fixtures/mutating_routes.json like every
route that writes, and on Stream R's public-mutations allowlist when the owner-area
middleware lands (it must stay reachable without a session: it is how a visitor subscribes).

What a caller can learn from this route is deliberately nothing: the same 200 whether the
address was new, already listed, or a bot filled the hidden field; a 422 names only the
field that failed shape; a 429 says to try later. There is no GET, no count and no lookup.
The address is never logged; the log line carries the row id and the form's source.
"""

import logging

from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session

from app.db import get_db
from app.models.editorial import SubscribeIn, SubscribeOut
from app.services import subscribers
from app.services.throttle import Throttle, client_key

log = logging.getLogger(__name__)

router = APIRouter(prefix="/api/subscribers", tags=["subscribers"])

# Five attempts per address in ten minutes, sixty for everyone per minute: a person retrying
# a typo is fine; a script is not.
throttle = Throttle(per_key=5, per_key_seconds=600, total=60, total_seconds=60)

THANKS = "Thanks, you are on the list. We will only write when there is something worth reading."


@router.post("", response_model=SubscribeOut, status_code=200)
def create_subscription(
    payload: SubscribeIn, request: Request, db: Session = Depends(get_db)
) -> SubscribeOut:
    if not throttle.allow(client_key(request)):
        raise HTTPException(
            status_code=429, detail="Too many attempts. Please try again in a few minutes."
        )
    if payload.website:
        # The honeypot was filled: answer as a success, store nothing.
        return SubscribeOut(message=THANKS)
    try:
        row = subscribers.subscribe(db, payload)
    except subscribers.InvalidSubscription as exc:
        raise HTTPException(status_code=422, detail=str(exc)) from exc
    log.info("subscriber %s recorded (source=%s)", row.id, row.source)
    return SubscribeOut(message=THANKS)
