import logging
from dataclasses import dataclass, field
from enum import StrEnum

from aiohttp.hdrs import AUTHORIZATION
from aiohttp.web_exceptions import HTTPNotFound

from http_tools import AbmServiceConnector
from init_helpers.dict_to_dataclass import dict_to_dataclass

logger = logging.getLogger(__name__)


@dataclass
class User:
    first_name: str
    last_name: str
    middle_name: str
    phone: str
    email: str
    login: str
    authority_roles: list[str] = field(default_factory=list)
    district_ext_ids: list[str] = field(default_factory=list)
    camera_group_ext_ids: list[str] = field(default_factory=list)
    type: str | None = None
    organization: str | None = None
    organization_area: str | None = None
    organization_unit: str | None = None
    position: str | None = None
    home_district_id: str | None = None


@dataclass
class UserAnswer:
    ext_id: str
    ticket: str


@dataclass(kw_only=True)
class UserWithId(User):
    id: str


class UserState(StrEnum):
    ACTIVE = "ACTIVE"
    PROCESSING = "PROCESSING"
    BLOCKED = "BLOCKED"


@dataclass(kw_only=True)
class StatefulUser(User):
    state: UserState


@dataclass(kw_only=True)
class StatefulUserWithId(StatefulUser):
    ext_id: str


@dataclass
class CameraGroup:
    name: str
    description: str | None = None


@dataclass(kw_only=True)
class CameraGroupWithId(CameraGroup):
    ext_id: str
    name: str
    description: str | None = None


@dataclass
class Camera:
    ext_id: str
    name: str


@dataclass
class Role:
    authority: str
    description: str
    title: str


@dataclass
class RoleAccess:
    code: str
    access: bool


@dataclass
class HomeDistrict:
    id: str
    name: str


@dataclass
class DistrictWithoutName:
    ext_id: str
    code: str


@dataclass
class District(DistrictWithoutName):
    name: str


class NetrisGatewayConnector:
    @dataclass(kw_only=True)
    class Config(AbmServiceConnector.Config):
        pass

    @dataclass
    class Context(AbmServiceConnector.Context):
        project_name: str

    def __init__(self, config: Config, context: Context) -> None:
        self._config = config
        self._context = context
        self._connector = AbmServiceConnector(config, context)

    @staticmethod
    def _construct_bearer_auth_header(token: str) -> dict[str, str]:
        return {AUTHORIZATION: f"Bearer {token}"}

    async def create_user(self, token: str, value: User) -> UserAnswer:
        answer = await self._connector.post(
            "/user/create", {"value": value}, headers=self._construct_bearer_auth_header(token))
        return dict_to_dataclass(answer, UserAnswer)

    async def update_user(self, token: str, value: UserWithId) -> UserAnswer:
        answer = await self._connector.post(
            "/user/update", {"value": value}, headers=self._construct_bearer_auth_header(token))
        return dict_to_dataclass(answer, UserAnswer)

    async def get_user(self, token: str, id: str) -> StatefulUser | None:
        try:
            answer = await self._connector.post(
                "/user/get", {"id": id}, headers=self._construct_bearer_auth_header(token))
        except HTTPNotFound:
            return None
        return dict_to_dataclass(answer, StatefulUser)

    async def get_user_by_login(self, token: str, login: str) -> StatefulUserWithId | None:
        try:
            answer = await self._connector.post(
                "/user/get_by_login", {"login": login}, headers=self._construct_bearer_auth_header(token))
        except HTTPNotFound:
            return None
        return dict_to_dataclass(answer, StatefulUserWithId)

    async def block_user(self, token: str, id: str) -> bool:
        answer = await self._connector.post(
            "/user/block", {"id": id}, headers=self._construct_bearer_auth_header(token))
        assert isinstance(answer, bool)
        return answer

    async def unblock_user(self, token: str, id: str) -> bool:
        answer = await self._connector.post(
            "/user/unblock", {"id": id}, headers=self._construct_bearer_auth_header(token))
        assert isinstance(answer, bool)
        return answer

    async def create_camera_group(self, token: str, value: CameraGroup) -> CameraGroupWithId:
        answer = await self._connector.post(
            "/camera_group/create", {"value": value}, headers=self._construct_bearer_auth_header(token))
        return dict_to_dataclass(answer, CameraGroupWithId)

    async def get_user_camera_group(self, token: str, id: str, group_name: str) -> list[CameraGroupWithId] | None:
        try:
            answer = await self._connector.post(
                "/user/camera_group/get", {"id": id, "group_name": group_name},
                headers=self._construct_bearer_auth_header(token))
        except HTTPNotFound:
            return None
        return [dict_to_dataclass(camera_group, CameraGroupWithId) for camera_group in answer] if answer else None

    async def update_camera_group(self, token: str, value: CameraGroupWithId) -> CameraGroupWithId:
        answer = await self._connector.post(
            "/camera_group/update", {"value": value}, headers=self._construct_bearer_auth_header(token))
        return dict_to_dataclass(answer, CameraGroupWithId)

    async def delete_camera_group(self, token: str, id: str) -> bool:
        answer = await self._connector.post(
            "/camera_group/delete", {"id": id}, headers=self._construct_bearer_auth_header(token))
        assert isinstance(answer, bool)
        return answer

    async def get_cameras_in_group(self, token: str, id: str) -> list[Camera]:
        answer = await self._connector.post(
            "/camera_group/camera/get", {"id": id}, headers=self._construct_bearer_auth_header(token))
        return [dict_to_dataclass(camera, Camera) for camera in answer]

    async def add_cameras_to_group(self, token: str, id: str, camera_ids: list[str]) -> bool:
        answer = await self._connector.post(
            "/camera_group/camera/add", {"id": id, "camera_ids": camera_ids},
            headers=self._construct_bearer_auth_header(token))
        assert isinstance(answer, bool)
        return answer

    async def update_cameras_in_group(self, token: str, id: str, camera_ids: list[str]) -> bool:
        """full camera_ids list required"""
        answer = await self._connector.post(
            "/camera_group/camera/update", {"id": id, "camera_ids": camera_ids},
            headers=self._construct_bearer_auth_header(token))
        assert isinstance(answer, bool)
        return answer

    async def delete_cameras_from_group(self, token: str, id: str, camera_ids: list[str]) -> bool:
        answer = await self._connector.post(
            "/camera_group/camera/delete", {"id": id, "camera_ids": camera_ids},
            headers=self._construct_bearer_auth_header(token))
        assert isinstance(answer, bool)
        return answer

    async def get_roles(self, token: str) -> list[Role]:
        answer = await self._connector.post(
            "/role/get", {}, headers=self._construct_bearer_auth_header(token))
        return [dict_to_dataclass(role, Role) for role in answer]

    async def get_home_districts(self, token: str) -> list[HomeDistrict]:
        answer = await self._connector.post(
            "/home_district/get", {}, headers=self._construct_bearer_auth_header(token))
        return [dict_to_dataclass(home_district, HomeDistrict) for home_district in answer]

    async def get_role_access(self, token: str, roles: list[str]) -> list[RoleAccess]:
        answer = await self._connector.post(
            "/role_access/get", {"roles": roles}, headers=self._construct_bearer_auth_header(token))
        return [dict_to_dataclass(role_access, RoleAccess) for role_access in answer]

    async def get_districts(self, token: str) -> list[District]:
        answer = await self._connector.post(
            "/district/get", {}, headers=self._construct_bearer_auth_header(token))
        return [dict_to_dataclass(district, District) for district in answer]

    async def get_district_access(self, token: str) -> list[DistrictWithoutName]:
        answer = await self._connector.post(
            "/district_access/get", {}, headers=self._construct_bearer_auth_header(token))
        return [dict_to_dataclass(district, DistrictWithoutName) for district in answer]
