"""Analytics endpoints for admin dashboard."""
import uuid
from datetime import datetime, timedelta, timezone
from typing import Optional

from fastapi import APIRouter, Depends, Query
from sqlalchemy import func, select, and_
from sqlalchemy.ext.asyncio import AsyncSession

from models.sql.presentation import PresentationModel
from models.sql.user import UserModel
from models.sql.job import JobModel
from services.access_service import get_accessible_client_ids
from services.database import get_async_session
from utils.auth_dependencies import require_client_admin

ANALYTICS_ROUTER = APIRouter(tags=["Analytics"])


async def _resolve_client_filter(
    client_id: Optional[uuid.UUID],
    user: UserModel,
    session: AsyncSession,
):
    """Return SQLAlchemy filter for presentation client scoping.

    super_admin with no filter → no restriction (sees all including NULL client_id).
    client_admin → scoped to their accessible clients.
    Explicit client_id → filter to that client only.
    """
    if client_id:
        return PresentationModel.client_id == client_id

    if user.role == "super_admin":
        # Super admin sees everything — no client filter needed
        return None

    cids = await get_accessible_client_ids(user, session)
    if not cids:
        # No accessible clients — return impossible condition
        return PresentationModel.client_id == None  # noqa: E711
    if len(cids) == 1:
        return PresentationModel.client_id == cids[0]
    return PresentationModel.client_id.in_(cids)


@ANALYTICS_ROUTER.get("/analytics/overview")
async def analytics_overview(
    client_id: Optional[uuid.UUID] = Query(None),
    current_user: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Aggregated stats: total presentations, active users, etc."""
    now = datetime.now(timezone.utc)
    month_ago = now - timedelta(days=30)
    week_ago = now - timedelta(days=7)

    cf = await _resolve_client_filter(client_id, current_user, session)

    base_filters = [PresentationModel.deleted_at.is_(None)]
    if cf is not None:
        base_filters.append(cf)

    base = select(PresentationModel).where(and_(*base_filters))

    # Total presentations
    total_q = select(func.count()).select_from(base.subquery())
    total = (await session.execute(total_q)).scalar() or 0

    # This month
    month_q = select(func.count()).select_from(
        base.where(PresentationModel.created_at >= month_ago).subquery()
    )
    this_month = (await session.execute(month_q)).scalar() or 0

    # This week
    week_q = select(func.count()).select_from(
        base.where(PresentationModel.created_at >= week_ago).subquery()
    )
    this_week = (await session.execute(week_q)).scalar() or 0

    # Active users (last 30 days)
    active_filters = [
        PresentationModel.deleted_at.is_(None),
        PresentationModel.created_at >= month_ago,
        PresentationModel.owner_id.isnot(None),
    ]
    if cf is not None:
        active_filters.append(cf)
    active_q = select(func.count(func.distinct(PresentationModel.owner_id))).where(
        and_(*active_filters)
    )
    active_users = (await session.execute(active_q)).scalar() or 0

    # Approval rate
    reviewed_filters = [
        PresentationModel.deleted_at.is_(None),
        PresentationModel.status.in_(["approved", "in_review"]),
    ]
    if cf is not None:
        reviewed_filters.append(cf)
    reviewed_q = select(func.count()).where(and_(*reviewed_filters))
    approved_filters = [
        PresentationModel.deleted_at.is_(None),
        PresentationModel.status == "approved",
    ]
    if cf is not None:
        approved_filters.append(cf)
    approved_q = select(func.count()).where(and_(*approved_filters))
    reviewed = (await session.execute(reviewed_q)).scalar() or 0
    approved = (await session.execute(approved_q)).scalar() or 0
    approval_rate = round((approved / reviewed * 100) if reviewed > 0 else 0, 1)

    return {
        "total_presentations": total,
        "this_month": this_month,
        "this_week": this_week,
        "active_users": active_users,
        "approval_rate": approval_rate,
    }


@ANALYTICS_ROUTER.get("/analytics/usage")
async def analytics_usage(
    client_id: Optional[uuid.UUID] = Query(None),
    days: int = Query(30, ge=7, le=90),
    current_user: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Usage metrics: decks per day time series, top users."""
    now = datetime.now(timezone.utc)
    since = now - timedelta(days=days)

    cf = await _resolve_client_filter(client_id, current_user, session)

    # Decks per day
    daily_filters = [
        PresentationModel.deleted_at.is_(None),
        PresentationModel.created_at >= since,
    ]
    if cf is not None:
        daily_filters.append(cf)
    daily_q = (
        select(
            func.date(PresentationModel.created_at).label("day"),
            func.count().label("count"),
        )
        .where(and_(*daily_filters))
        .group_by(func.date(PresentationModel.created_at))
        .order_by(func.date(PresentationModel.created_at))
    )
    daily_result = await session.execute(daily_q)
    daily = [{"date": str(row.day), "count": row.count} for row in daily_result]

    # Top users
    top_filters = [
        PresentationModel.deleted_at.is_(None),
        PresentationModel.owner_id.isnot(None),
        PresentationModel.created_at >= since,
    ]
    if cf is not None:
        top_filters.append(cf)
    top_users_q = (
        select(
            PresentationModel.owner_id,
            func.count().label("count"),
        )
        .where(and_(*top_filters))
        .group_by(PresentationModel.owner_id)
        .order_by(func.count().desc())
        .limit(10)
    )
    top_result = await session.execute(top_users_q)
    top_users = [
        {"user_id": str(row.owner_id), "count": row.count} for row in top_result
    ]

    return {
        "daily": daily,
        "top_users": top_users,
    }


@ANALYTICS_ROUTER.get("/analytics/quality")
async def analytics_quality(
    client_id: Optional[uuid.UUID] = Query(None),
    current_user: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Quality metrics: status distribution, average comments."""
    cf = await _resolve_client_filter(client_id, current_user, session)

    base_filter = [PresentationModel.deleted_at.is_(None)]
    if cf is not None:
        base_filter.append(cf)

    # Status distribution
    status_q = (
        select(
            PresentationModel.status,
            func.count().label("count"),
        )
        .where(and_(*base_filter))
        .group_by(PresentationModel.status)
    )
    status_result = await session.execute(status_q)
    status_dist = {row.status or "draft": row.count for row in status_result}

    # Presentations with comments
    with_comments_q = select(func.count()).where(
        and_(
            *base_filter,
            PresentationModel.review_comment.isnot(None),
        )
    )
    with_comments = (await session.execute(with_comments_q)).scalar() or 0

    return {
        "status_distribution": status_dist,
        "presentations_with_comments": with_comments,
    }


@ANALYTICS_ROUTER.get("/analytics/performance")
async def analytics_performance(
    client_id: Optional[uuid.UUID] = Query(None),
    current_user: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """Performance metrics: job durations and error rates."""
    # Build job filter
    job_filter = []
    if client_id:
        job_filter.append(JobModel.client_id == client_id)
    elif current_user.role != "super_admin":
        cids = await get_accessible_client_ids(current_user, session)
        if cids:
            if len(cids) == 1:
                job_filter.append(JobModel.client_id == cids[0])
            else:
                job_filter.append(JobModel.client_id.in_(cids))

    # Job status distribution
    job_status_q = (
        select(
            JobModel.status,
            func.count().label("count"),
        )
        .where(and_(*job_filter)) if job_filter else
        select(
            JobModel.status,
            func.count().label("count"),
        )
    ).group_by(JobModel.status)
    job_result = await session.execute(job_status_q)
    job_status = {row.status: row.count for row in job_result}

    # Average generation time (completed jobs only)
    avg_base = [
        JobModel.status == "completed",
        JobModel.started_at.isnot(None),
        JobModel.completed_at.isnot(None),
    ]
    if job_filter:
        avg_base.extend(job_filter)
    avg_time_q = select(
        func.avg(
            func.extract("epoch", JobModel.completed_at)
            - func.extract("epoch", JobModel.started_at)
        )
    ).where(and_(*avg_base))
    avg_seconds = (await session.execute(avg_time_q)).scalar()
    avg_generation_time = round(avg_seconds, 1) if avg_seconds else None

    # Error rate
    total_jobs = sum(job_status.values()) if job_status else 0
    failed_jobs = job_status.get("failed", 0)
    error_rate = round((failed_jobs / total_jobs * 100) if total_jobs > 0 else 0, 1)

    return {
        "job_status_distribution": job_status,
        "avg_generation_time_seconds": avg_generation_time,
        "error_rate": error_rate,
        "total_jobs": total_jobs,
    }


@ANALYTICS_ROUTER.get("/analytics/ai-usage")
async def analytics_ai_usage(
    client_id: Optional[uuid.UUID] = Query(None),
    days: int = Query(30, ge=7, le=90),
    current_user: UserModel = Depends(require_client_admin),
    session: AsyncSession = Depends(get_async_session),
):
    """AI model usage: total calls, tokens consumed, breakdown by provider."""
    from models.sql.ai_usage import AIUsageModel

    since = datetime.now(timezone.utc) - timedelta(days=days)

    base_filter = [AIUsageModel.created_at >= since]
    if client_id:
        base_filter.append(AIUsageModel.client_id == client_id)
    elif current_user.role != "super_admin":
        cids = await get_accessible_client_ids(current_user, session)
        if cids:
            if len(cids) == 1:
                base_filter.append(AIUsageModel.client_id == cids[0])
            else:
                base_filter.append(AIUsageModel.client_id.in_(cids))

    # Totals
    totals_q = select(
        func.count().label("total_calls"),
        func.coalesce(func.sum(AIUsageModel.input_tokens), 0).label("total_input_tokens"),
        func.coalesce(func.sum(AIUsageModel.output_tokens), 0).label("total_output_tokens"),
        func.coalesce(func.sum(AIUsageModel.total_tokens), 0).label("total_tokens"),
    ).where(and_(*base_filter))
    totals = (await session.execute(totals_q)).one()

    # By provider
    by_provider_q = (
        select(
            AIUsageModel.provider,
            func.count().label("calls"),
            func.coalesce(func.sum(AIUsageModel.total_tokens), 0).label("tokens"),
        )
        .where(and_(*base_filter))
        .group_by(AIUsageModel.provider)
    )
    provider_result = await session.execute(by_provider_q)
    by_provider = [
        {"provider": row.provider, "calls": row.calls, "tokens": row.tokens}
        for row in provider_result
    ]

    # By model
    by_model_q = (
        select(
            AIUsageModel.model,
            func.count().label("calls"),
            func.coalesce(func.sum(AIUsageModel.total_tokens), 0).label("tokens"),
        )
        .where(and_(*base_filter))
        .group_by(AIUsageModel.model)
        .order_by(func.count().desc())
        .limit(10)
    )
    model_result = await session.execute(by_model_q)
    by_model = [
        {"model": row.model, "calls": row.calls, "tokens": row.tokens}
        for row in model_result
    ]

    # Daily usage
    daily_q = (
        select(
            func.date(AIUsageModel.created_at).label("day"),
            func.count().label("calls"),
            func.coalesce(func.sum(AIUsageModel.total_tokens), 0).label("tokens"),
        )
        .where(and_(*base_filter))
        .group_by(func.date(AIUsageModel.created_at))
        .order_by(func.date(AIUsageModel.created_at))
    )
    daily_result = await session.execute(daily_q)
    daily = [
        {"date": str(row.day), "calls": row.calls, "tokens": row.tokens}
        for row in daily_result
    ]

    return {
        "total_calls": totals.total_calls,
        "total_input_tokens": totals.total_input_tokens,
        "total_output_tokens": totals.total_output_tokens,
        "total_tokens": totals.total_tokens,
        "by_provider": by_provider,
        "by_model": by_model,
        "daily": daily,
    }
