from datetime import datetime
from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, select
from sqlalchemy.orm import relationship
from db import CRUD, Base
from db.schemas.signings import Signing
from utils import answer, logger, toDict


class Statuses(Base):
    __tablename__ = "statuses"

    id = Column(Integer, primary_key=True, index=True)
    singingId = Column(Integer, ForeignKey("signings.id", ondelete="CASCADE"))
    idPatientMis = Column(
        String,
        ForeignKey("patients.idPatientMis", ondelete="CASCADE"),
        nullable=True,
    )
    patient = relationship(
        "Patient",
        cascade="all, delete-orphan",
        lazy="joined",
        uselist=False,
        single_parent=True,
    )
    status = Column(Integer)
    created_at = Column(DateTime, default=datetime.now)

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

    def toDict(self):
        return toDict(self)

    async def save(self) -> "Statuses":
        return await CRUD.create(self, refresh=True)

    async def edit(self, **kwargs) -> "Statuses":
        return await CRUD.update(Statuses, self.id, **kwargs)

    @staticmethod
    async def addStatus(singingId: int, status: int, idPatientMis: str = None):
        if type(singingId) != int:
            singingId = int(singingId)
        if type(status) != int:
            status = int(status)
        if idPatientMis is not None and type(idPatientMis) != str:
            idPatientMis = str(idPatientMis)
        await CRUD.create(
            Statuses(singingId=singingId, status=status, idPatientMis=idPatientMis)
        )

    @staticmethod
    async def getStatus(singingId: int) -> list:
        if type(singingId) != int:
            singingId = int(singingId)
        return await CRUD.read(
            select(Statuses)
            .where(Statuses.singingId == singingId)
            .order_by(Statuses.created_at),
            True,
        )

    @staticmethod
    async def updateStatus(singingId: int, isNew: bool = False):
        from api.n3health import getTrackingIDSigning

        if type(singingId) != int:
            singingId = int(singingId)

        singingData = await Signing.getSigningById(singingId, False)
        if not singingData.success:
            logger.error(singingData.message)
            return singingData

        statusesDB = await Statuses.getStatus(singingId)

        latestStatusData = {}

        if len(statusesDB) > 0:
            for statusDB in statusesDB:
                idPatientMis = statusDB.idPatientMis
                if not idPatientMis:
                    idPatientMis = "global"
                if idPatientMis not in latestStatusData:
                    latestStatusData[idPatientMis] = 0
                if statusDB.status > latestStatusData[idPatientMis]:
                    latestStatusData[idPatientMis] = statusDB.status

        statusData = await getTrackingIDSigning(singingData.data.trackingId, singingId)
        if not statusData.success and not isNew:
            logger.error(statusData.message)
            return statusData

        actualStatusData = {}

        if statusData.data:
            actualStatusData = statusData.data
        elif isNew:
            actualStatusData = {"global": 207, singingData.data.idPatientMis: 201}
            for recipient in singingData.data.recipients:
                actualStatusData[recipient] = 201

        if not actualStatusData:
            logger.error("Нет данных о статусе")
            return answer()

        for idPatientMis, actualStatusCode in actualStatusData.items():
            if idPatientMis == "storagePath":
                continue
            latestStatusCode = latestStatusData.get(idPatientMis, 0)

            if latestStatusCode != actualStatusCode:
                logger.info(
                    f"⚠️ Обновился статус подписания #{singingId} для '{idPatientMis}': {actualStatusCode}"
                )
                await Statuses.addStatus(
                    singingId,
                    actualStatusCode,
                    idPatientMis if idPatientMis != "global" else None,
                )

                if (
                    "storagePath" in actualStatusData
                    and len(actualStatusData["storagePath"]) > 0
                ):
                    logger.info(f"✅ Подписание #{singingId} завершено успешно")
                    await singingData.data.edit(
                        storagePath=actualStatusData["storagePath"]
                    )

                elif actualStatusCode in [213, 498, 499, 500, 501]:
                    logger.warning(
                        f"❌ Подписание #{singingId} завершено не успешно: {actualStatusCode}"
                    )
                    await singingData.data.edit(trackingId=None)

                elif actualStatusCode in [205, 206, 212]:
                    logger.warning(
                        f"❌ Подписание #{singingId} завершено не успешно для '{idPatientMis}': {actualStatusCode}"
                    )
            else:
                logger.info(
                    f"📌 Не обновился статус подписания #{singingId} для '{idPatientMis}': {latestStatusCode}"
                )

        return answer()
