"""Manifest domain — repository."""
from __future__ import annotations

from datetime import datetime, timezone
from typing import Optional
from uuid import UUID

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

from src.domain.manifest.models import ManifestRun


class ManifestRunRepository:
    def __init__(self, db: AsyncSession) -> None:
        self._db = db

    async def create(self, run: ManifestRun) -> ManifestRun:
        self._db.add(run)
        await self._db.flush()
        await self._db.refresh(run)
        return run

    async def get_by_id(self, run_id: UUID) -> Optional[ManifestRun]:
        result = await self._db.execute(
            select(ManifestRun).where(ManifestRun.id == run_id)
        )
        return result.scalar_one_or_none()

    async def get_by_token_hash(self, token_hash: str) -> Optional[ManifestRun]:
        result = await self._db.execute(
            select(ManifestRun).where(ManifestRun.token_hash == token_hash)
        )
        return result.scalar_one_or_none()

    async def list_for_project(
        self, project_id: UUID, limit: int = 20, offset: int = 0
    ) -> list[ManifestRun]:
        result = await self._db.execute(
            select(ManifestRun)
            .where(ManifestRun.project_id == project_id)
            .order_by(ManifestRun.created_at.desc())
            .limit(limit)
            .offset(offset)
        )
        return list(result.scalars().all())

    async def complete(
        self, run_id: UUID, snapshot_hash: Optional[str] = None
    ) -> Optional[ManifestRun]:
        await self._db.execute(
            update(ManifestRun)
            .where(ManifestRun.id == run_id)
            .values(
                status="completed",
                completed_at=datetime.now(timezone.utc),
                manifest_snapshot_hash=snapshot_hash,
            )
        )
        await self._db.flush()
        return await self.get_by_id(run_id)

    async def fail(self, run_id: UUID, reason: str) -> Optional[ManifestRun]:
        await self._db.execute(
            update(ManifestRun)
            .where(ManifestRun.id == run_id)
            .values(
                status="failed",
                completed_at=datetime.now(timezone.utc),
                failed_reason=reason,
            )
        )
        await self._db.flush()
        return await self.get_by_id(run_id)

    async def expire_stale(self) -> int:
        """Mark all active runs past their expires_at as expired. Returns count."""
        now = datetime.now(timezone.utc)
        result = await self._db.execute(
            update(ManifestRun)
            .where(ManifestRun.status == "active", ManifestRun.expires_at < now)
            .values(status="expired")
        )
        await self._db.flush()
        return result.rowcount
