import enum
from typing import Optional

from asyncpg import Connection

from maps_adv.common.helpers import Converter
from maps_adv.stat_controller.server.lib.db import DbTaskStatus

from . import base


class TaskStatus(enum.Enum):
    accepted = "accepted"
    completed = "completed"


converter = Converter(
    (
        (TaskStatus.accepted, DbTaskStatus.accepted_by_collector),
        (TaskStatus.completed, DbTaskStatus.collected),
    )
)


class TaskManager(base.BlockingManager):
    name: str = "collector"

    async def update(
        self, executor_id: str, task_id: int, status: TaskStatus, con=None
    ):
        async with self.connection(con) as con:
            await self._update(executor_id, task_id, status, con)

    async def find_last_available(self, con=None) -> Optional[dict]:
        async with self.connection(con) as con:
            return await base.find_last_task(DbTaskStatus.charged, con)

    async def retrieve_details(self, task_id: int) -> dict:
        async with self._db.acquire() as con:
            row = await base.retrieve_task_details(task_id, con)

        row["status"] = converter.reversed(row["status"])
        return row

    async def reanimate_last_failed(self, executor_id: str, con=None) -> Optional[dict]:
        sql = (
            "SELECT tasks.id, tasks.timing_from, tasks.timing_to "
            "FROM tasks JOIN tasks_log ON tasks.current_log_id = tasks_log.id "
            "WHERE tasks.status IS NULL AND tasks_log.status = $1 "
            "ORDER BY tasks.timing_to DESC "
            "LIMIT 1"
        )

        async with self.connection(con) as con:
            row = await con.fetchrow(sql, DbTaskStatus.accepted_by_collector)
            if row:
                await self._update(executor_id, row["id"], TaskStatus.accepted, con)

        if row:
            return dict(row)

    async def _update(
        self, executor_id: str, task_id: int, status: TaskStatus, con: Connection
    ):
        db_status = converter.forward(status)

        sql = (
            "with tasks_log_rows as ("
            "INSERT INTO tasks_log (task_id, executor_id, status) "
            "VALUES ($1, $2, $3) RETURNING id, status"
            ")"
            "UPDATE tasks "
            "SET current_log_id = tasks_log_rows.id, status = tasks_log_rows.status "
            "FROM tasks_log_rows WHERE tasks.id = $1"
        )

        await con.execute(sql, task_id, executor_id, db_status)
