"""Analytics service.

Provides functions for recording and querying analytics events.

⚠️ AUTO-GENERATED BY PRISM — DO NOT EDIT
"""

from __future__ import annotations

import logging
from datetime import datetime, timedelta
from typing import Any

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from {{ project_name }}.models.analytics import AnalyticsEvent
{% if track_user_sessions %}
from {{ project_name }}.models.analytics import AnalyticsSession
{% endif %}

logger = logging.getLogger("{{ project_name }}.analytics.service")

# Module-level reference for the async session factory
_session_factory = None


def configure_session_factory(factory: Any) -> None:
    """Configure the async session factory for background event recording."""
    global _session_factory
    _session_factory = factory


async def record_event(
    event_name: str,
    category: str = "custom",
    properties: dict[str, Any] | None = None,
    user_id: str | None = None,
    session_id: str | None = None,
    page_url: str | None = None,
    db: AsyncSession | None = None,
) -> None:
    """Record an analytics event.

    Can be called with or without an existing database session.
    When called without a session (e.g., from the decorator), uses
    the configured session factory.
    """
{% if anonymize_users %}
    if user_id:
        import hashlib

        user_id = hashlib.sha256(user_id.encode()).hexdigest()[:16]
{% endif %}

    event = AnalyticsEvent(
        event_name=event_name,
        category=category,
        properties=properties,
        user_id=user_id,
        session_id=session_id,
        page_url=page_url,
        timestamp=datetime.utcnow(),
    )

    if db is not None:
        db.add(event)
        await db.flush()
        return

    if _session_factory is not None:
        try:
            async with _session_factory() as session:
                session.add(event)
                await session.commit()
        except Exception:
            logger.debug("Failed to persist analytics event: %s", event_name)
    else:
        logger.debug("No session factory configured, event not persisted: %s", event_name)


async def get_events(
    db: AsyncSession,
    *,
    event_name: str | None = None,
    category: str | None = None,
    user_id: str | None = None,
    start_date: datetime | None = None,
    end_date: datetime | None = None,
    page: int = 1,
    page_size: int = 50,
) -> tuple[list[AnalyticsEvent], int]:
    """Query analytics events with filtering and pagination."""
    query = select(AnalyticsEvent)
    count_query = select(func.count(AnalyticsEvent.id))

    if event_name:
        query = query.where(AnalyticsEvent.event_name == event_name)
        count_query = count_query.where(AnalyticsEvent.event_name == event_name)
    if category:
        query = query.where(AnalyticsEvent.category == category)
        count_query = count_query.where(AnalyticsEvent.category == category)
    if user_id:
        query = query.where(AnalyticsEvent.user_id == user_id)
        count_query = count_query.where(AnalyticsEvent.user_id == user_id)
    if start_date:
        query = query.where(AnalyticsEvent.timestamp >= start_date)
        count_query = count_query.where(AnalyticsEvent.timestamp >= start_date)
    if end_date:
        query = query.where(AnalyticsEvent.timestamp <= end_date)
        count_query = count_query.where(AnalyticsEvent.timestamp <= end_date)

    total = (await db.execute(count_query)).scalar() or 0

    query = query.order_by(AnalyticsEvent.timestamp.desc())
    query = query.offset((page - 1) * page_size).limit(page_size)
    result = await db.execute(query)
    events = list(result.scalars().all())

    return events, total

{% if dashboard_enabled %}


async def get_summary(
    db: AsyncSession,
    *,
    days: int = {{ retention_days }},
) -> dict[str, Any]:
    """Get analytics summary for the dashboard."""
    since = datetime.utcnow() - timedelta(days=days)

    # Total events
    total_events = (
        await db.execute(
            select(func.count(AnalyticsEvent.id)).where(
                AnalyticsEvent.timestamp >= since
            )
        )
    ).scalar() or 0

    # Total page views
    total_page_views = (
        await db.execute(
            select(func.count(AnalyticsEvent.id)).where(
                AnalyticsEvent.event_name == "page_view",
                AnalyticsEvent.timestamp >= since,
            )
        )
    ).scalar() or 0

    # Unique users
    unique_users = (
        await db.execute(
            select(func.count(func.distinct(AnalyticsEvent.user_id))).where(
                AnalyticsEvent.user_id.is_not(None),
                AnalyticsEvent.timestamp >= since,
            )
        )
    ).scalar() or 0

    # Top events
    top_events_result = await db.execute(
        select(
            AnalyticsEvent.event_name,
            func.count(AnalyticsEvent.id).label("count"),
        )
        .where(AnalyticsEvent.timestamp >= since)
        .group_by(AnalyticsEvent.event_name)
        .order_by(func.count(AnalyticsEvent.id).desc())
        .limit(10)
    )
    top_events = [
        {"event_name": row.event_name, "count": row.count}
        for row in top_events_result
    ]

    # Top pages
    top_pages_result = await db.execute(
        select(
            AnalyticsEvent.page_url,
            func.count(AnalyticsEvent.id).label("count"),
        )
        .where(
            AnalyticsEvent.event_name == "page_view",
            AnalyticsEvent.page_url.is_not(None),
            AnalyticsEvent.timestamp >= since,
        )
        .group_by(AnalyticsEvent.page_url)
        .order_by(func.count(AnalyticsEvent.id).desc())
        .limit(10)
    )
    top_pages = [
        {"page_url": row.page_url, "count": row.count}
        for row in top_pages_result
    ]

    # Events by day
    events_by_day_result = await db.execute(
        select(
            func.date_trunc("day", AnalyticsEvent.timestamp).label("day"),
            func.count(AnalyticsEvent.id).label("count"),
        )
        .where(AnalyticsEvent.timestamp >= since)
        .group_by(func.date_trunc("day", AnalyticsEvent.timestamp))
        .order_by(func.date_trunc("day", AnalyticsEvent.timestamp))
    )
    events_by_day = [
        {"day": row.day.isoformat(), "count": row.count}
        for row in events_by_day_result
    ]

{% if track_user_sessions %}
    # Session stats
    total_sessions = (
        await db.execute(
            select(func.count(AnalyticsSession.id)).where(
                AnalyticsSession.started_at >= since
            )
        )
    ).scalar() or 0

    avg_duration = (
        await db.execute(
            select(func.avg(AnalyticsSession.duration_seconds)).where(
                AnalyticsSession.started_at >= since,
                AnalyticsSession.duration_seconds.is_not(None),
            )
        )
    ).scalar()
{% else %}
    total_sessions = 0
    avg_duration = None
{% endif %}

    return {
        "total_events": total_events,
        "total_sessions": total_sessions,
        "total_page_views": total_page_views,
        "unique_users": unique_users,
        "avg_session_duration": round(avg_duration, 2) if avg_duration else None,
        "top_events": top_events,
        "top_pages": top_pages,
        "events_by_day": events_by_day,
    }
{% endif %}
