from datetime import UTC, datetime
from logging import getLogger

from fastapi import status as st
from httpx import AsyncClient, BasicAuth
from orjson import dumps, loads

from core.config import settings
from shared import exceptions as e
from shared.redis import client as cache

from . import schema as s


class VITACORE_API(AsyncClient):
    def __init__(self):
        self.logger = getLogger(__name__)
        super().__init__(base_url=settings.VITACORE_BASE_URL)

    async def get_token(self):
        token = await self.get_cache('vitacore_token')

        if token is None:
            token = await self.login()
            await self.set_cache(
                'vitacore_token', dumps({'token': token}).decode(), 10800
            )

            return token

        return token['token']

    async def get_cache(self, key: str):
        data = await cache.get(key)

        if data:
            return loads(data.decode())

        return None

    async def set_cache(self, key: str, value: str, ttl: int = 600):
        await cache.set(key, value, ttl)

    async def login(self):
        req = await self.post(
            '/auth',
            auth=BasicAuth(
                settings.VITACORE_USERNAME, settings.VITACORE_PASSWORD
            ),
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return req.text

            case _:
                self.logger.error(req.text)
                raise e.UnknownException

    async def findBySnils(self, snils: str):
        data = await self.get_cache(f'vitacore_findBySnils2:{snils}')

        if data:
            return s.PatientModel.model_validate(data)

        token = await self.get_token()
        req = await self.get(
            '/findBySnils',
            params={'snils': snils},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                model = s.PatientModel.model_validate(req.json())
                await self.set_cache(
                    f'vitacore_findBySnils2:{snils}',
                    model.model_dump_json(),
                    14400,
                )
                return model

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getProfile(self, patId: str):
        data = await self.get_cache(f'vitacore_getProfile:{patId}')

        if data:
            return s.ProfileModel.model_validate(data)

        token = await self.get_token()
        req = await self.get(
            '/getProfile',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                model = s.ProfileModel.model_validate(req.json())
                await self.set_cache(
                    f'vitacore_getProfile:{patId}',
                    model.model_dump_json(),
                    14400,
                )
                return model
            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getDepartments(self):
        data = await self.get_cache('vitacore_getDepartments')

        if data:
            return s.OrganizationsModel.model_validate(data)

        token = await self.get_token()
        req = await self.get(
            '/getDepartments', headers={'Authorization': f'Bearer {token}'}
        )

        match req.status_code:
            case st.HTTP_200_OK:
                model = s.OrganizationsModel.model_validate(req.json())
                await self.set_cache(
                    'vitacore_getDepartments',
                    model.model_dump_json(),
                    14400,
                )
                return model
            case _:
                self.logger.error(req.text)
                raise e.UnknownException

    async def getWorkers(self, departmentId: str):
        data = await self.get_cache(f'vitacore_getWorkers:{departmentId}')

        if data:
            return s.WorkersModel.model_validate(data)

        token = await self.get_token()
        req = await self.get(
            '/getWorkers',
            params={'departmentId': departmentId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                data = s.WorkersModel.model_validate(req.json())
                await self.set_cache(
                    f'vitacore_getWorkers:{departmentId}',
                    data.model_dump_json(),
                    14400,
                )
                return data

            case st.HTTP_206_PARTIAL_CONTENT:
                return s.WorkersModel(Workers=[])

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getSpecsV021(self):
        data = await self.get_cache('vitacore_getSpecsV021')

        if data:
            return s.SpecsV021Model.model_validate(data)

        token = await self.get_token()
        req = await self.get(
            '/getSpecsV021', headers={'Authorization': f'Bearer {token}'}
        )

        match req.status_code:
            case st.HTTP_200_OK:
                data = s.SpecsV021Model.model_validate(req.json())
                await self.set_cache(
                    'vitacore_getSpecsV021',
                    data.model_dump_json(),
                    14400,
                )
                return data
            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getEntries(self, patId: str):
        token = await self.get_token()
        if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
            patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'

        req = await self.get(
            '/getEntries',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.EntriesModel.model_validate(req.json())
            case st.HTTP_206_PARTIAL_CONTENT:
                error = s.ErrorModel.model_validate(req.json())

                if error.error == 'Не найдены записи по указанному patId':
                    return s.EntriesModel(Entries=[])

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getVaccsReport(self, patId: str):
        token = await self.get_token()
        if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
            patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'

        req = await self.get(
            '/getVaccsReport',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.VaccsReportModel.model_validate(req.json())
            case st.HTTP_206_PARTIAL_CONTENT:
                error = s.ErrorModel.model_validate(req.json())

                if (
                    error.error == 'Не найдены записи по указанному patId'
                    or error.error
                    == 'Не найдены вакцинации по данному пациенту'
                ):
                    return s.VaccsReportModel(content='')
            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getMedExamDict(self):
        data = await self.get_cache('vitacore_getMedExamDict')

        if data:
            return s.MedExamDictModel.model_validate(data)

        token = await self.get_token()
        req = await self.get(
            '/getMedExamDict', headers={'Authorization': f'Bearer {token}'}
        )

        match req.status_code:
            case st.HTTP_200_OK:
                data = s.MedExamDictModel.model_validate(req.json())
                await self.set_cache(
                    'vitacore_getMedExamDict',
                    data.model_dump_json(),
                    14400,
                )
                return data
            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getRoutesList(self, patId: str):
        token = await self.get_token()
        req = await self.get(
            '/getRoutesList',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.RoutesListModel.model_validate(req.json())

            case st.HTTP_206_PARTIAL_CONTENT:
                error = s.ErrorModel.model_validate(req.json())

                if error.error == 'Не найдены случаи по указанному patId':
                    return s.RoutesListModel(
                        EventID='none',
                        EventDate=datetime.now(UTC),
                        LpuName='',
                        Routes=[],
                    )

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getHospExaminations(self, patId: str, examId: str | None = None):
        if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
            patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'

        token = await self.get_token()
        req = await self.get(
            '/getHospExaminations',
            params={'patId': patId, 'examId': examId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.HospExaminationsModel.model_validate(req.json())

            case st.HTTP_206_PARTIAL_CONTENT:
                error = s.ErrorModel.model_validate(req.json())

                if error.error == 'Пациент не госпитализирован!':
                    return s.HospExaminationsModel(
                        EventID='none',
                        EventDate=datetime.now(UTC),
                        LpuName='',
                        Examinations=[],
                    )

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getCurrHosp(self, patId: str):
        token = await self.get_token()
        req = await self.get(
            '/getCurrHosp',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.CurHospitalizationsModel.model_validate(req.json())

            case st.HTTP_206_PARTIAL_CONTENT:
                return s.CurHospitalizationsModel(
                    Hospitalizations=[],
                )

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getHosps(self, patId: str):
        token = await self.get_token()
        if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
            patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'

        req = await self.get(
            '/getHosps',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.HospitalizationsModel.model_validate(req.json())

            case st.HTTP_206_PARTIAL_CONTENT:
                error = s.ErrorModel.model_validate(req.json())

                if (
                    error.error
                    == 'Не найдены госпитализации по указанному patId'
                ):
                    return s.HospitalizationsModel(
                        Hospitalizations=[],
                    )

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getHospRecommendations(self, patId: str):
        token = await self.get_token()
        if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
            patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'

        req = await self.get(
            '/getHospRecommendations',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.HospRecommendationsModel.model_validate(req.json())

            case st.HTTP_206_PARTIAL_CONTENT:
                error = s.ErrorModel.model_validate(req.json())

                if (
                    error.error
                    == 'Не найдены госпитализации по указанному patId'
                ):
                    return s.HospRecommendationsModel(
                        EventID='none',
                        EventDate=datetime.now(UTC),
                        Recommendations=[],
                    )

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getHospRoutes(self, patId: str):
        token = await self.get_token()
        if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
            patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'

        req = await self.get(
            '/getHospRoutes',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.HospRoutesModel.model_validate(req.json())

            case st.HTTP_206_PARTIAL_CONTENT:
                error = s.ErrorModel.model_validate(req.json())

                if error.error == 'Пациент не госпитализирован!':
                    return s.HospRoutesModel(
                        EventID='none',
                        EventDate=datetime.now(UTC),
                        RoutesToDoctor=[],
                        RoutesToDiagnostic=[],
                    )

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getDiagnosticResults(self, patId: str):
        token = await self.get_token()
        req = await self.get(
            '/getDiagnosticResults',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.DiagnosticResultsModel.model_validate(req.json())
            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getELNs(self, patId: str):
        token = await self.get_token()
        req = await self.get(
            '/getELNs',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.ELNsModel.model_validate(req.json())

            case st.HTTP_206_PARTIAL_CONTENT:
                error = s.ErrorModel.model_validate(req.json())

                if error.error == 'Пациент не госпитализирован!':
                    return s.ELNsModel(PatientELNs=[])

            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getPatFLG(self, patId: str):
        token = await self.get_token()
        req = await self.get(
            '/getPatFLG',
            params={'patId': patId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.PatientFLGModel.model_validate(req.json())
            case _:
                self.logger.error(req.json())
                raise e.UnknownException

    async def getDiagResultFile(self, resultId: str):
        token = await self.get_token()
        req = await self.get(
            '/getDiagResultFile',
            params={'resultId': resultId},
            headers={'Authorization': f'Bearer {token}'},
        )

        match req.status_code:
            case st.HTTP_200_OK:
                return s.DiagResultFileModel.model_validate(req.json())

            case st.HTTP_206_PARTIAL_CONTENT:
                error = s.ErrorModel.model_validate(req.json())

                if (
                    error.error == 'Не найдены проведенные исследования по '
                    'данному идентификатору'
                ):
                    return s.DiagResultFileModel(content='')

            case _:
                self.logger.error(req.json())
                raise e.UnknownException
