"""Celery task: expire stale cart sessions every 15 minutes."""
from __future__ import annotations

import logging

from src.worker.celery_app import celery_app

logger = logging.getLogger(__name__)


@celery_app.task(name="cart_plugin.expire_cart_sessions")
def expire_cart_sessions_task():
    """
    Bulk UPDATE cart_sessions SET status='EXPIRED'
    WHERE status='PENDING' AND expires_at < now().
    Emits cart.session_expired event for each expired session.
    """
    from src.core.database import SessionCelery
    from src.apps.cart_plugin import crud as cart_crud

    try:
        with SessionCelery() as db:
            # Capture expired sessions before bulk-updating so we can emit events.
            from sqlalchemy import select
            from datetime import datetime, timezone
            from src.apps.cart_plugin.models.cart_session import CartSession

            now = datetime.now(timezone.utc)
            stmt = select(CartSession).where(
                CartSession.status == "PENDING",
                CartSession.expires_at < now,
            )
            sessions_to_expire = list(db.execute(stmt).scalars().all())

            count = cart_crud.expire_stale_sessions(db)
            logger.info("expire_cart_sessions: expired %d session(s)", count)

        # Emit events outside the DB context (best-effort).
        if sessions_to_expire:
            import asyncio
            from src.events.base import BaseEvent
            from src.events.dispatcher import EventDispatcher

            async def _emit_events():
                for s in sessions_to_expire:
                    try:
                        await EventDispatcher.dispatch(
                            BaseEvent(
                                event_type="cart.session_expired",
                                data={
                                    "cart_session_id": s.id,
                                    "merchant_id": s.merchant_id,
                                },
                            )
                        )
                    except Exception as exc:
                        logger.warning(
                            "cart.session_expired event dispatch failed for session %s: %s",
                            s.id,
                            exc,
                        )

            try:
                loop = asyncio.get_event_loop()
                if loop.is_running():
                    loop.create_task(_emit_events())
                else:
                    loop.run_until_complete(_emit_events())
            except Exception as exc:
                logger.warning("expire_cart_sessions: event loop error: %s", exc)

        return {"expired": count}
    except Exception as exc:
        logger.error("expire_cart_sessions: error: %s", exc)
        raise
