"""Job status update helpers."""

from __future__ import annotations

from collections.abc import Callable
from typing import TYPE_CHECKING, cast

from noteflow.infrastructure.logging import get_logger, log_state_transition
from noteflow.infrastructure.persistence.repositories import DiarizationJob

from ...proto import noteflow_pb2
from ..errors import ERR_CANCELLED_BY_USER
from ._types import DIARIZATION_TIMEOUT_SECONDS

if TYPE_CHECKING:
    from ..protocols import ServicerHost

logger = get_logger(__name__)


def _job_status_name(status: int) -> str:
    name_fn = cast(Callable[[int], str], noteflow_pb2.JobStatus.Name)
    return name_fn(status)


class JobStatusMixin:
    """Mixin providing job status update operations."""

    async def update_job_completed(
        self: ServicerHost,
        job_id: str,
        job: DiarizationJob | None,
        updated_count: int,
        speaker_ids: list[str],
    ) -> None:
        """Update job status to COMPLETED."""
        old_status = job.status if job else noteflow_pb2.JOB_STATUS_RUNNING
        new_status = noteflow_pb2.JOB_STATUS_COMPLETED
        async with self.create_repository_provider() as repo:
            if not repo.supports_diarization_jobs:
                logger.error("Cannot update job %s: database required", job_id)
                return
            await repo.diarization_jobs.update_status(
                job_id,
                new_status,
                segments_updated=updated_count,
                speaker_ids=speaker_ids,
            )
            await repo.commit()
        log_state_transition(
            "diarization_job",
            job_id,
            _job_status_name(old_status),
            _job_status_name(int(new_status)),
            segments_updated=updated_count,
        )

    async def handle_job_timeout(
        self: ServicerHost,
        job_id: str,
        job: DiarizationJob | None,
        meeting_id: str | None,
    ) -> None:
        """Handle job timeout."""
        old_status = job.status if job else noteflow_pb2.JOB_STATUS_RUNNING
        new_status = noteflow_pb2.JOB_STATUS_FAILED
        error_msg = f"Job timed out after {DIARIZATION_TIMEOUT_SECONDS} seconds"
        logger.error(
            "Diarization job %s timed out after %ds for meeting %s",
            job_id,
            DIARIZATION_TIMEOUT_SECONDS,
            meeting_id,
        )
        async with self.create_repository_provider() as repo:
            if not repo.supports_diarization_jobs:
                logger.error("Cannot update job %s: database required", job_id)
                return
            await repo.diarization_jobs.update_status(
                job_id,
                new_status,
                error_message=error_msg,
            )
            await repo.commit()
        log_state_transition(
            "diarization_job",
            job_id,
            _job_status_name(old_status),
            _job_status_name(int(new_status)),
            reason="timeout",
        )

    async def handle_job_cancelled(
        self: ServicerHost,
        job_id: str,
        job: DiarizationJob | None,
        meeting_id: str | None,
    ) -> None:
        """Handle job cancellation."""
        old_status = job.status if job else noteflow_pb2.JOB_STATUS_RUNNING
        new_status = noteflow_pb2.JOB_STATUS_CANCELLED
        logger.info("Diarization job %s cancelled for meeting %s", job_id, meeting_id)
        async with self.create_repository_provider() as repo:
            if not repo.supports_diarization_jobs:
                logger.error("Cannot update job %s: database required", job_id)
                return
            await repo.diarization_jobs.update_status(
                job_id,
                new_status,
                error_message=ERR_CANCELLED_BY_USER,
            )
            await repo.commit()
        log_state_transition(
            "diarization_job",
            job_id,
            _job_status_name(old_status),
            _job_status_name(int(new_status)),
            reason="user_cancelled",
        )

    async def handle_job_failed(
        self: ServicerHost,
        job_id: str,
        job: DiarizationJob | None,
        meeting_id: str | None,
        exc: Exception,
    ) -> None:
        """Handle job failure."""
        old_status = job.status if job else noteflow_pb2.JOB_STATUS_RUNNING
        new_status = noteflow_pb2.JOB_STATUS_FAILED
        logger.exception("Diarization failed for meeting %s", meeting_id)
        async with self.create_repository_provider() as repo:
            if not repo.supports_diarization_jobs:
                logger.error("Cannot update job %s: database required", job_id)
                return
            await repo.diarization_jobs.update_status(
                job_id,
                new_status,
                error_message=str(exc),
            )
            await repo.commit()
        log_state_transition(
            "diarization_job",
            job_id,
            _job_status_name(old_status),
            _job_status_name(int(new_status)),
            reason="exception",
        )
