from __future__ import annotations

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

from app.db.models import CrawlRun, RunStatus, RunTriggerType


async def list_recent_runs_for_user(
    db_session: AsyncSession,
    *,
    user_id: int,
    limit: int = 20,
) -> list[CrawlRun]:
    result = await db_session.execute(
        select(CrawlRun)
        .where(CrawlRun.user_id == user_id)
        .order_by(CrawlRun.start_dt.desc(), CrawlRun.id.desc())
        .limit(limit)
    )
    return list(result.scalars().all())


async def list_runs_for_user(
    db_session: AsyncSession,
    *,
    user_id: int,
    limit: int = 100,
    failed_only: bool = False,
) -> list[CrawlRun]:
    stmt = (
        select(CrawlRun)
        .where(CrawlRun.user_id == user_id)
        .order_by(CrawlRun.start_dt.desc(), CrawlRun.id.desc())
        .limit(limit)
    )
    if failed_only:
        stmt = stmt.where(CrawlRun.status.in_([RunStatus.FAILED, RunStatus.PARTIAL_FAILURE]))
    result = await db_session.execute(stmt)
    return list(result.scalars().all())


async def get_run_for_user(
    db_session: AsyncSession,
    *,
    user_id: int,
    run_id: int,
) -> CrawlRun | None:
    result = await db_session.execute(
        select(CrawlRun).where(
            CrawlRun.user_id == user_id,
            CrawlRun.id == run_id,
        )
    )
    return result.scalar_one_or_none()


async def get_manual_run_by_idempotency_key(
    db_session: AsyncSession,
    *,
    user_id: int,
    idempotency_key: str,
) -> CrawlRun | None:
    result = await db_session.execute(
        select(CrawlRun)
        .where(
            CrawlRun.user_id == user_id,
            CrawlRun.trigger_type == RunTriggerType.MANUAL,
            or_(
                CrawlRun.idempotency_key == idempotency_key,
                CrawlRun.error_log["meta"]["idempotency_key"].astext == idempotency_key,
            ),
        )
        .order_by(CrawlRun.start_dt.desc(), CrawlRun.id.desc())
        .limit(1)
    )
    return result.scalar_one_or_none()
