from abc import ABC, abstractmethod
from datetime import datetime
from typing import List, Optional

from smb.common.pgswim import PoolType, SwimEngine

from maps_adv.geosmb.cleaner.server.lib import sqls


class BaseDataManager(ABC):
    __slots__ = ()

    @abstractmethod
    async def register_client_for_delete(
        self, external_id: str, passport_uid: int
    ) -> None:
        raise NotImplementedError

    @abstractmethod
    async def list_not_processed_requests(self) -> List[dict]:
        raise NotImplementedError

    @abstractmethod
    async def mark_request_as_processed(
        self, record_id: int, processed_at: datetime
    ) -> None:
        raise NotImplementedError

    @abstractmethod
    async def retrieve_last_request(self, passport_uid: int) -> dict:
        raise NotImplementedError

    @abstractmethod
    async def retrieve_dt_of_last_processed_request(
        self, passport_uid: int
    ) -> Optional[datetime]:
        raise NotImplementedError

    @abstractmethod
    async def list_processed_services(self, request_id: int) -> set:
        raise NotImplementedError

    @abstractmethod
    async def create_operation(
        self, request_id: int, service_name: str, metadata: dict, is_success: bool
    ) -> None:
        raise NotImplementedError


class DataManager(BaseDataManager):
    __slots__ = ("_db",)
    _db: SwimEngine

    def __init__(self, db: SwimEngine):
        self._db = db

    async def register_client_for_delete(
        self, external_id: str, passport_uid: int
    ) -> None:
        async with self._db.acquire() as con:
            await con.execute(sqls.save_delete_request, external_id, passport_uid)

    async def list_not_processed_requests(self) -> List[dict]:
        async with self._db.acquire(PoolType.replica) as con:
            rows = await con.fetch(sqls.list_not_processed_records)

            return [dict(row) for row in rows]

    async def mark_request_as_processed(
        self, record_id: int, processed_at: datetime
    ) -> None:
        async with self._db.acquire() as con:
            await con.execute(sqls.mark_record_as_processed, record_id, processed_at)

    async def retrieve_last_request(self, passport_uid: int) -> Optional[dict]:
        async with self._db.acquire(pool_type=PoolType.replica) as con:
            row = await con.fetchrow(sqls.retrieve_last_request, passport_uid)

            return dict(row) if row else None

    async def retrieve_dt_of_last_processed_request(
        self, passport_uid: int
    ) -> Optional[datetime]:
        async with self._db.acquire(pool_type=PoolType.replica) as con:
            return await con.fetchval(
                sqls.retrieve_dt_of_last_processed_request, passport_uid
            )

    async def list_processed_services(self, request_id: int) -> set:
        async with self._db.acquire(pool_type=PoolType.replica) as con:
            rows = await con.fetch(sqls.list_processed_services, request_id)

            return {row["service_name"] for row in rows}

    async def create_operation(
        self, request_id: int, service_name: str, metadata: dict, is_success: bool
    ) -> None:
        async with self._db.acquire() as con:
            await con.execute(
                sqls.create_operation, request_id, service_name, metadata, is_success
            )
