from datetime import datetime

from persistence import Connection

from application.model import (
    UpdateStatus,
    UpdateType,
    UserUpdateData,
)
from application.model.configuration import Configurations
from application.model.customexception import ErrorInsertingNewUpdateVersion
from boss_api import Boss
from boss_api.shared.response import UserDto
from mbcontextmessagehandler import MbContextMessageBus
from mwhelper import BaseRepository
from typing import (
    List,
    Optional,
)
from ._CommonPOSRepository import CommonPOSRepository


class UserUpdaterPOSRepository(BaseRepository):

    def __init__(
        self,           # type: UserUpdaterPOSRepository
        message_bus,    # type: MbContextMessageBus
        configs,        # type: Configurations
    ):
        # type: (...) -> None

        super(UserUpdaterPOSRepository, self).__init__(message_bus.mbcontext)

        self.message_bus = message_bus
        self.configs = configs
        self.update_type = UpdateType.user
        self.update_value = self.update_type.value
        self.local_configs = configs.updaters[self.update_type.name]
        self.common_pos_repository = CommonPOSRepository(message_bus)
        self.boss_service = Boss(message_bus=message_bus)

    def get_pos_users(
        self,   # type: UserUpdaterPOSRepository
    ):
        # type: (...) -> List[UserDto]

        return self.boss_service.get_users_list()

    def update_users(
        self,           # type: UserUpdaterPOSRepository
        updated_users,  # type: List[UserUpdateData]
    ):
        # type: (...) -> None

        updated_users = [user.to_boss_user_dto() for user in updated_users]
        self.boss_service.insert_or_update_users(users=updated_users)

    def insert_new_user_update(self, update_id, update_name):
        # type: (str, str) -> None

        try:
            def inner_func(conn):
                # type: (Connection) -> None

                date_now = str(datetime.now().isoformat())
                query = """
                INSERT INTO UpdatesController
                (UpdateId, UpdateName, TypeId, StatusId, ObtainedDate, DownloadedDate, BackupDate, AppliedDate,
                NotifiedDate)
                VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', '{4}', '{4}', '{4}', '{4}')"""
                query_format = query.format(update_id, update_name, self.update_value, UpdateStatus.applied, date_now)
                conn.query(query_format)

            self.execute_with_connection(inner_func, service_name=self.configs.persistence_name)
        except Exception as ex:
            raise ErrorInsertingNewUpdateVersion(ex)

    def get_last_user_update_id(self):
        # type: () -> int

        def inner_func(conn):
            # type: (Connection) -> int

            query = """
                SELECT UpdateId
                FROM UpdatesController
                WHERE TypeId = '{}' AND AppliedDate IS NOT NULL
                ORDER BY Id DESC
                LIMIT 1""".format(self.update_value)

            cursor = conn.select(query)
            row = cursor.get_row(0)

            if row:
                update_id = row.get_entry("UpdateId")
                return int(update_id)

            return 0

        return self.execute_with_connection(inner_func, service_name=self.configs.persistence_name)

    def has_already_applied_update(self):
        # type: () -> bool

        return self.common_pos_repository.has_already_applied_update(self.configs.persistence_name, self.update_value)

    def disable_users(
        self,           # type: UserUpdaterPOSRepository
        users_disable,  # type: List[str]
    ):
        # type: (...) -> None

        users = self.boss_service.get_users_list()
        for user in users:
            if str(user.user_id) in users_disable:
                user.status = 1
                user.termination_date = datetime.now().date().isoformat()

        users_to_disable = [user for user in users if user.status == 1]
        self.boss_service.insert_or_update_users(users=users_to_disable)

    def get_last_user_update_date(self):
        # type: () -> Optional[datetime]

        return self.common_pos_repository.get_last_update_date(self.configs.persistence_name, self.update_value)
