from __future__ import annotations

from typing import Optional
from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession

from src.apps.notifications.constants import NotificationScope, NotificationType
from src.apps.notifications.services.notification_service import NotificationService
from src.core.websocket import PLATFORM_CHANNEL, manager


def _truncate(text: Optional[str], limit: int = 140) -> str:
    """Message previews go straight into the bell dropdown, so keep them short."""
    if not text:
        return ""
    collapsed = " ".join(text.split())
    return collapsed if len(collapsed) <= limit else collapsed[: limit - 1].rstrip() + "…"


async def notify_new_inquiry(
    db: AsyncSession,
    *,
    tenant_id: UUID,
    inquiry,
) -> dict:
    """Create the tenant-scoped notification for a new cemetery inquiry and
    return the WebSocket payload to broadcast once the transaction commits.

    Covers both `contact_form` and `plot_availability` sources — they land in
    the same `contact_inquiries` table and the same Sales → Inbox screen.
    """
    is_availability = inquiry.source_type == "plot_availability"
    if is_availability:
        notification_type = NotificationType.ENQUIRY_PLOT_AVAILABILITY
        plot_label = f" for plot {inquiry.plot_number}" if inquiry.plot_number else ""
        title = f"New plot enquiry{plot_label}"
    else:
        notification_type = NotificationType.ENQUIRY_CONTACT_FORM
        title = "New contact enquiry"

    sender = inquiry.sender_name or inquiry.sender_email or "Someone"
    body = _truncate(inquiry.message) or f"{sender} submitted an enquiry."

    notification = await NotificationService(db).create(
        scope=NotificationScope.TENANT,
        tenant_id=tenant_id,
        type=notification_type,
        title=f"{title} from {sender}",
        body=body,
        entity_type="contact_inquiry",
        entity_id=inquiry.id,
        link=f"/portal/sales?tab=inbox&inquiry={inquiry.id}",
    )
    return NotificationService.build_ws_payload(notification)


async def notify_new_website_enquiry(
    db: AsyncSession,
    *,
    enquiry,
) -> dict:
    """Create the platform-scoped notification for a marketing-site enquiry
    (contact form or book-a-call) and return the payload to broadcast.

    Platform scope means `tenant_id` stays NULL — these enquiries belong to
    INDELIS, not to any cemetery.
    """
    enquiry_type = getattr(enquiry, "type", None)
    label = "Book-a-call request" if enquiry_type == "call" else "New website enquiry"
    sender = getattr(enquiry, "name", None) or getattr(enquiry, "email", None) or "Someone"
    body = _truncate(getattr(enquiry, "message", None)) or f"{sender} contacted INDELIS."

    notification = await NotificationService(db).create(
        scope=NotificationScope.PLATFORM,
        type=NotificationType.ENQUIRY_MARKETING,
        title=f"{label} from {sender}",
        body=body,
        entity_type="website_enquiry",
        entity_id=enquiry.id,
        link=f"/admin/enquiries?enquiry={enquiry.id}",
    )
    return NotificationService.build_ws_payload(notification)


async def broadcast_to_tenant(tenant_id: UUID, payload: dict) -> None:
    """Post-commit broadcast. Scheduled via FastAPI BackgroundTasks so a
    rolled-back request never announces a notification that does not exist.
    Swallows failures: a dropped notification must not surface as a 500 on a
    public enquiry form that has already been saved."""
    try:
        await manager.broadcast(str(tenant_id), payload)
    except Exception:
        pass


async def broadcast_to_platform(payload: dict) -> None:
    """Post-commit broadcast to every connected site admin. See above for why
    failures are swallowed."""
    try:
        await manager.broadcast(PLATFORM_CHANNEL, payload)
    except Exception:
        pass
