from datetime import UTC, datetime
from json import dumps
from logging import getLogger
from urllib.parse import quote, urlencode

from fastapi import UploadFile
from fastapi import status as st
from httpx import AsyncClient

from core.config import settings
from shared import exceptions as e

from . import schema as s


class TDN_API(AsyncClient):
    def __init__(self):
        self.logger = getLogger(__name__)
        super().__init__(
            base_url=settings.TDN_BASE_URL,
            verify=False,
            headers={
                'Content-Type': 'application/json',
            },
        )

    async def signin(self):
        data = {
            'username': settings.TDN_LOGIN,
            'password': settings.TDN_PASSWORD,
        }

        res = await self.post('/core/auth/signin', json=data)

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

    async def patient_search(self, access_token: str, vitaId: str):
        query = (
            quote(dumps({'where': {'vitaId': vitaId}}))
            .replace('%7B', '{')
            .replace('%7D', '}')
        )

        req = await self.get(
            f'/ddn/patients/search?query={query}',
            headers={'Authorization': f'Bearer {access_token}'},
        )

        return req.json()

    async def observations_search(self, access_token: str, patientUid: str):
        query = (
            quote(dumps({'where': {'patientUid': patientUid}}))
            .replace('%7B', '{')
            .replace('%7D', '}')
        )

        res = await self.get(
            f'/ddn/observations/search?query={query}',
            headers={'Authorization': f'Bearer {access_token}'},
        )

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

    async def observations_measurement_search(
        self, access_token: str, observationUid: str
    ):
        encoded_query = urlencode(
            {
                'query': dumps(
                    {
                        'where': {'observationUid': observationUid},
                        'relations': [
                            'measurement',
                            'obsrvMtMetrics',
                            'obsrvMtMetrics.metric',
                        ],
                    }
                )
            }
        )

        res = await self.get(
            f'/ddn/observation/obsrv-measurements/search?{encoded_query}',
            headers={'Authorization': f'Bearer {access_token}'},
        )

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

    async def create_series(
        self, access_token: str, observationUid: str, obsrvMeasurementUid: str
    ):
        now = datetime.now(UTC)
        tz_offset = now.strftime('%z')
        formatted_offset = (
            f'{tz_offset[:3]}:{tz_offset[3:]}' if tz_offset else '+0000'
        )
        date_str = (
            f'{now.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]} {formatted_offset}'
        )

        res = await self.patch(
            '/ddn/observation/series',
            headers={'Authorization': f'Bearer {access_token}'},
            json={
                'observationUid': observationUid,
                'obsrvMeasurementUid': obsrvMeasurementUid,
                'date': date_str,
            },
        )

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

    async def create_series_values(
        self,
        access_token: str,
        seriesUid: str,
        obsrvMtMetricUid: str,
        *,
        nvalue: int | None = None,
        fvalue: float | None = None,
        svalue: str | None = None,
    ):
        data: dict[str, str | int | float] = {
            'seriesUid': seriesUid,
            'obsrvMtMetricUid': obsrvMtMetricUid,
        }

        if nvalue is not None:
            data['nvalue'] = nvalue

        if fvalue is not None:
            data['fvalue'] = fvalue

        if svalue is not None:
            data['svalue'] = svalue

        res = await self.patch(
            '/ddn/observation/series-values',
            headers={'Authorization': f'Bearer {access_token}'},
            json=data,
        )

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

    async def ekg(
        self,
        access_token: str,
        patientUid: str,
        serial_number: str,
        ecg_file: UploadFile,
    ):
        req = await self.post(
            '/ddn/observation/series-values/ecg/krb02',
            headers={
                'Authorization': f'Bearer {access_token}',
                'Content-Type': 'multipart/form-data; boundary=+++',
            },
            data={
                'patientUid': patientUid,
                'serialNumber': serial_number,
            },
            files={
                'ecg': (
                    ecg_file.filename,
                    ecg_file.file,
                    ecg_file.content_type or 'application/octet-stream',
                )
            },
        )

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