from typing import Optional

from maps_adv.stat_controller.server.lib.data_managers.collector import (
    TaskManager,
    TaskStatus,
)

from .exceptions import InProgressByAnotherExecutor, StatusSequenceViolation

__all__ = [
    "Domain",
    "InProgressByAnotherExecutor",
    "StatusSequenceViolation",
    "TaskStatus",
]


class Domain:
    __slots__ = ("_dm",)

    _dm: TaskManager

    _status_sequences = {TaskStatus.accepted: TaskStatus.completed}

    def __init__(self, dm: TaskManager) -> Optional[dict]:
        self._dm = dm

    async def find_new(self, executor_id: str) -> Optional[dict]:
        async with self._dm.lock() as con:
            got = await self._dm.find_last_available(con)
            if got:
                await self._dm.update(executor_id, got["id"], TaskStatus.accepted, con)
            else:
                got = await self._dm.reanimate_last_failed(executor_id, con)
            return got

    async def update(self, executor_id: str, task_id: int, status: TaskStatus) -> dict:
        details = await self._dm.retrieve_details(task_id)

        # raise for invalid status
        current_status = details["status"]
        if self._status_sequences.get(current_status) != status:
            raise StatusSequenceViolation(
                task_id=task_id,
                executor_id=executor_id,
                current_status=current_status,
                target_status=status,
            )

        # raise when trying to change task_uid
        current_executor_id = details["executor_id"]
        if current_executor_id != executor_id:
            raise InProgressByAnotherExecutor(
                task_id=task_id,
                status=status,
                current_executor_id=current_executor_id,
                executor_id=executor_id,
            )

        await self._dm.update(executor_id, task_id, status)

        return {
            "id": details["id"],
            "timing_from": details["timing_from"],
            "timing_to": details["timing_to"],
        }
