import uuid
from typing import Any, cast

from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import select

from app.models.notification import Notification
from app.models.restaurant import Restaurant, get_notification_preferences


async def create_notification(
    session: AsyncSession,
    restaurant_id: str,
    type: str,
    title: str,
    body: str | None = None,
    metadata: dict[str, Any] | None = None,
    idempotency_key: str | None = None,
) -> Notification | None:
    restaurant_result = await session.execute(
        select(Restaurant).where(Restaurant.id == restaurant_id)
    )
    restaurant = restaurant_result.scalar_one_or_none()
    if restaurant is None:
        return None

    preferences = get_notification_preferences(restaurant.settings)
    if not preferences.get("in_app_enabled", True):
        return None

    events = preferences.get("events")
    if isinstance(events, dict) and not bool(events.get(type, True)):
        return None

    if idempotency_key:
        restaurant_id_col = cast(Any, Notification.restaurant_id)
        metadata_col = cast(Any, Notification.metadata_json)
        existing_result = await session.execute(
            select(Notification)
            .where(
                restaurant_id_col == restaurant_id,
                metadata_col["idempotency_key"].as_string() == idempotency_key,
            )
            .limit(1)
        )
        existing = existing_result.scalar_one_or_none()
        if existing is not None:
            return existing

    payload = dict(metadata or {})
    if idempotency_key:
        payload.setdefault("idempotency_key", idempotency_key)

    notification = Notification(
        id=str(uuid.uuid4()),
        restaurant_id=restaurant_id,
        type=type,
        title=title,
        body=body,
        metadata_json=payload or None,
    )
    session.add(notification)
    await session.commit()
    return notification
