from collections.abc import Sequence
from uuid import UUID

from sqlalchemy.orm import selectinload
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

from ..models import Job
from ..models import JobStatus
from .base import ServiceBase


class JobService(ServiceBase):
    """Thin service class for managing jobs in the database."""

    async def create_job(self, job: Job, session: AsyncSession | None = None) -> Job:
        """Create a new job in the database."""
        async with self._session_scope(session) as _session:
            _session.add(job)
            await _session.commit()
            await _session.refresh(job)
            return job

    async def read_job(self, job_id: UUID, session: AsyncSession | None = None) -> Job:
        """Read an existing job from the database.

        The selectinload option is used to eagerly load the related document for the job, which makes the full Document
        model object available in the return Job object.
        """
        async with self._session_scope(session) as _session:
            job = await _session.get(
                Job,
                job_id,
                # Makes the full Document model object available in the return Job object
                options=(selectinload(Job.document),),  # pyright: ignore[reportArgumentType]
            )
        if job is None:
            raise ValueError(f"Job with id {job_id} not found")
        return job

    async def update_job(self, job: Job, session: AsyncSession | None = None) -> Job:
        """Update an existing job in the database."""
        async with self._session_scope(session) as _session:
            await _session.merge(job)
            await _session.commit()
            await _session.refresh(job)
            return job

    async def delete_job(self, job: Job, session: AsyncSession | None = None) -> None:
        """Delete a job from the database."""
        async with self._session_scope(session) as _session:
            await _session.delete(job)
            await _session.commit()

    async def query_jobs(
        self,
        *,
        status: JobStatus | None = None,
        filename: str | None = None,
    ) -> Sequence[Job]:
        """Query jobs from the database based on provided filters."""
        async with self._session_scope(None) as session:
            query = select(Job)
            if status is not None:
                query = query.where(Job.status == status)
            if filename is not None:
                query = query.where(Job.document.filename == filename)
            return (await session.exec(query)).all()

    async def list_jobs(self, session: AsyncSession | None = None) -> Sequence[Job]:
        """List all jobs in the database."""
        async with self._session_scope(session) as _session:
            return (await _session.exec(select(Job))).all()

    async def mark_job_status(
        self,
        job_id: UUID,
        status: JobStatus,
        session: AsyncSession | None = None,
    ) -> Job:
        """Mark a job with a new status."""
        async with self._session_scope(session) as _session:
            job = await _session.get(Job, job_id)
            if job is None:
                raise ValueError(f"Job with id {job_id} not found")
            job.status = status
            await _session.commit()
            await _session.refresh(job)
            return job
