"""POST /send -- message send endpoint (RELAY-02)."""

from __future__ import annotations

import json

from fastapi import APIRouter, Depends, HTTPException, Request

from uam.protocol import (
    InvalidEnvelopeError,
    SignatureVerificationError,
    deserialize_verify_key,
    from_wire_dict,
    verify_envelope,
)
from uam.relay.auth import verify_api_key_http
from uam.relay.database import store_message
from uam.relay.models import SendRequest, SendResponse

router = APIRouter()


@router.post("/send", response_model=SendResponse)
async def send_message(
    body: SendRequest,
    request: Request,
    agent: dict = Depends(verify_api_key_http),
) -> SendResponse:
    """Send a signed message envelope via REST.

    Order of operations (DoS-resistant):
    1.  Auth (via dependency injection -- already done)
    2.  Blocklist check (SPAM-01 -- O(1) set lookup)
    3.  Allowlist check (SPAM-01 -- O(1), sets skip_reputation flag)
    4.  Adaptive sender rate limit (SPAM-04 -- reputation-based limit)
    5.  Parse envelope
    6.  Sender identity match
    7.  Domain rate limit (SPAM-03 -- relay domain exempt)
    8.  Recipient rate limit (RELAY-05)
    9.  Reputation score check (SPAM-06 -- drop if score <20)
    10. Signature verification (expensive -- LAST)
    11. Route or store
    """
    db = request.app.state.db
    manager = request.app.state.manager
    sender_limiter = request.app.state.sender_limiter
    recipient_limiter = request.app.state.recipient_limiter
    spam_filter = request.app.state.spam_filter
    reputation_manager = request.app.state.reputation_manager
    domain_limiter = request.app.state.domain_limiter
    settings = request.app.state.settings

    # Blocklist check (SPAM-01) -- O(1), before everything
    if spam_filter.is_blocked(agent["address"]):
        raise HTTPException(status_code=403, detail="Sender is blocked")

    # Allowlist check (SPAM-01) -- O(1), skip reputation-based limits
    is_allowlisted = spam_filter.is_allowed(agent["address"])

    # Adaptive sender rate limit (SPAM-04) -- use reputation-based limit
    if not is_allowlisted:
        send_limit = reputation_manager.get_send_limit(agent["address"])
        if send_limit == 0:
            raise HTTPException(status_code=403, detail="Sender reputation too low")
        if not sender_limiter.check(agent["address"], limit=send_limit):
            raise HTTPException(status_code=429, detail="Sender rate limit exceeded")
    else:
        # Allowlisted senders use default (full) rate limit
        if not sender_limiter.check(agent["address"]):
            raise HTTPException(status_code=429, detail="Sender rate limit exceeded")

    # Parse envelope
    try:
        envelope = from_wire_dict(body.envelope)
    except InvalidEnvelopeError as exc:
        raise HTTPException(status_code=400, detail=f"Invalid envelope: {exc}") from exc

    # Verify sender identity matches
    if envelope.from_address != agent["address"]:
        raise HTTPException(
            status_code=403,
            detail=f"Sender mismatch: envelope from '{envelope.from_address}' but authenticated as '{agent['address']}'",
        )

    # Domain rate limit (SPAM-03) -- by sender domain, relay domain exempt
    sender_domain = agent["address"].split("::")[1] if "::" in agent["address"] else ""
    if sender_domain and sender_domain != settings.relay_domain and not is_allowlisted:
        if not domain_limiter.check(sender_domain):
            raise HTTPException(status_code=429, detail="Domain rate limit exceeded")

    # Rate limit: recipient (RELAY-05)
    if not recipient_limiter.check(envelope.to_address):
        raise HTTPException(status_code=429, detail="Recipient rate limit exceeded (100/min)")

    # Reputation check (SPAM-06) -- single fast read before expensive crypto
    if not is_allowlisted:
        score = reputation_manager.get_score(agent["address"])
        if score < 20:
            raise HTTPException(status_code=403, detail="Sender reputation too low")

    # Verify signature (expensive -- only after cheap checks pass)
    try:
        sender_vk = deserialize_verify_key(agent["public_key"])
        verify_envelope(envelope, sender_vk)
    except SignatureVerificationError as exc:
        raise HTTPException(status_code=400, detail=f"Invalid signature: {exc}") from exc

    # Three-tier delivery chain: WebSocket > webhook > store-and-forward (HOOK-02)
    # Tier 1: WebSocket (real-time)
    delivered = await manager.send_to(envelope.to_address, body.envelope)
    delivery_method = "websocket" if delivered else None

    if not delivered:
        # Tier 2: Webhook (near-real-time)
        webhook_service = request.app.state.webhook_service
        webhook_initiated = await webhook_service.try_deliver(
            envelope.to_address, body.envelope
        )
        if webhook_initiated:
            delivery_method = "webhook"
            delivered = True  # webhook delivery initiated (async)

    if delivery_method is None:
        # Tier 3: Store-and-forward (eventual)
        await store_message(db, envelope.from_address, envelope.to_address, json.dumps(body.envelope))
        delivery_method = "stored"

    return SendResponse(message_id=envelope.message_id, delivered=(delivery_method != "stored"))
