from datetime import datetime
import secrets
from sqlalchemy import Column, DateTime, Integer, String, select
from db import Base, CRUD
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
import utils
from utils import logger

ph = PasswordHasher()


class Settings(Base):
    __tablename__ = "settings"

    id = Column(Integer, primary_key=True, index=True)
    hashedPassword = Column(String)
    actualToken = Column(String, nullable=True)
    medodsApiHost = Column(String, nullable=True)
    medodsApiPort = Column(Integer, nullable=True)
    medodsApiIdentity = Column(String, nullable=True)
    medodsApiSecretKey = Column(String, nullable=True)
    n3healthHost = Column(String, nullable=True)
    n3healthAutorization = Column(String, nullable=True)
    n3healthXIdLpu = Column(String, nullable=True)
    expirationAlert = Column(Integer, nullable=True)
    created_at = Column(DateTime, default=datetime.now)
    updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)

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

    def toDict(self):
        return utils.toDict(self)

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

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

    async def delete(self) -> bool:
        return await CRUD.delete(self)

    @staticmethod
    async def getSettings(toDict: bool = True, isCheck: bool = False):
        settings = await CRUD.read(select(Settings))
        if not settings:
            if not isCheck:
                utils.logger.error("Настройки не инициализированы")
            return utils.answer(success=False, message="Настройки не инициализированы")
        data = settings.toDict() if toDict else settings
        return utils.answer(data=data)

    @staticmethod
    async def initSettings():
        utils.logger.info("Инициализация настроек...")
        settings = await Settings.getSettings(False, True)
        if settings.success:
            utils.logger.info("Настройки уже инициализированы, обновляем...")
            await settings.data.delete()
        settingsData = {
            "hashedPassword": ph.hash("123"),
            "medodsApiHost": "188.64.134.62",
            "medodsApiPort": 3000,
            "medodsApiIdentity": "56ae4f7e-7053-41a7-816e-7b31d91f869f",
            "medodsApiSecretKey": "f90306f7abe623ec4394159c6ac2cbd3f0783a085d217bcc0a5a3d5ea7537902",
            "n3healthHost": "https://b2b-demo.n3health.ru/sep/api",
            "n3healthAutorization": "42e2080c-e33f-0800-3080-4e6989e3d97f",
            "n3healthXIdLpu": "8fc38b60-035b-462d-ad1b-31863e0fd2f0",
            "expirationAlert": 45,
        }
        settings = Settings(**settingsData)
        data = await settings.save()
        utils.logger.info("Настройки успешно инициализированы")
        return utils.answer(data=data)

    @staticmethod
    async def verifyPassword(password: str):
        settings = await Settings.getSettings(False)
        if not settings.success:
            utils.logger.error("Настройки не инициализированы")
            return utils.answer(success=False, message="Настройки не инициализированы")
        try:
            success = ph.verify(settings.data.hashedPassword, password)
            if success:
                newToken = secrets.token_hex(16)
                await settings.data.edit(actualToken=newToken)
                return utils.answer(data=newToken)
        except VerifyMismatchError:
            logger.error("Проверка пароля завершилась ошибкой")
        return utils.answer(success=False)

    @staticmethod
    async def verifyToken(token: str):
        settings = await Settings.getSettings(False)
        if not settings.success:
            utils.logger.error("Настройки не инициализированы")
            return utils.answer(success=False, message="Настройки не инициализированы")
        if settings.data.actualToken == token:
            return utils.answer()
        logger.error("Проверка токена завершилась ошибкой")
        return utils.answer(success=False)

    @staticmethod
    async def clearToken():
        settings = await Settings.getSettings(False)
        if not settings.success:
            utils.logger.error("Настройки не инициализированы")
            return utils.answer(success=False, message="Настройки не инициализированы")
        await settings.data.edit(actualToken=None)
        return utils.answer()

    @staticmethod
    async def updateSettings(**kwargs):
        if len(kwargs) == 0:
            utils.logger.error("Нет данных для обновления")
            return utils.answer(success=False, message="Нет данных для обновления")
        settings = await Settings.getSettings(False)
        if not settings.success:
            utils.logger.error("Настройки не инициализированы")
            return utils.answer(success=False, message="Настройки не инициализированы")
        if "password" in kwargs:
            kwargs["hashedPassword"] = ph.hash(kwargs.pop("password"))
        try:
            data = await settings.data.edit(**kwargs)
            return utils.answer(data=data)
        except Exception as e:
            utils.logger.error(str(e))
            return utils.answer(success=False, message=str(e))
