from datetime import datetime

from application.model import UpdateType, UserUpdateData, UpdateStatus, PosUser
from application.model.configuration import Configurations
from application.model.customexception import ErrorInsertingNewUpdateVersion
from mbcontextmessagehandler import MbContextMessageBus
from mwhelper import BaseRepository
from persistence import Connection
from helper import remove_accents
from typing import List, Optional
from ._CommonPOSRepository import CommonPOSRepository


class UserUpdaterPOSRepository(BaseRepository):

    def __init__(self, message_bus, configs):
        # type: (MbContextMessageBus, Configurations) -> 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)

    def get_pos_users(self):
        # type: () -> List[PosUser]

        def inner_func(conn):
            # type: (Connection) -> List[PosUser]

            users = []
            cursor = conn.select(
                """
                        SELECT UserId, UserName, LongName, Password, Level, Status, AdmissionDate, TerminationDate
                        FROM Users
                    """
            )
            for row in cursor:
                user_id = int(row.get_entry("UserId"))
                user_name = row.get_entry("UserName")
                long_name = row.get_entry("LongName")
                password = row.get_entry("Password")
                level = int(row.get_entry("Level"))
                status = bool(row.get_entry("Status"))
                admission_date = row.get_entry("AdmissionDate")
                admission = datetime.strptime(admission_date, '%Y-%m-%d') if admission_date else None
                termination_date = row.get_entry("TerminationDate")
                termination = datetime.strptime(termination_date, '%Y-%m-%d') if termination_date else None
                pos_user = PosUser(user_id, user_name, long_name, password, level, status, admission, termination)
                users.append(pos_user)

            return users

        return self.execute_with_connection(inner_func)

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

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

            for user in updated_users:
                if user.end_date is None:
                    user.end_date = "NULL"
                elif user.end_date != "NULL":
                    user.end_date = "'{}'".format(user.end_date)

                long_name = remove_accents(user.long_name)
                user_name = user.user_id
                query = """
                    INSERT OR REPLACE INTO Users
                    (
                        UserId,
                        UserName,
                        LongName,
                        Password,
                        Level,
                        Status,
                        AdmissionDate,
                        TerminationDate,
                        PayRate,
                        NfcUuid
                    )
                    VALUES ({0}, '{1}', '{2}', '{3}', {4}, {5}, '{6}', {7}, {8}, '{9}')""".format(
                    user.user_id,
                    user_name,
                    conn.escape(long_name),
                    user.password,
                    user.level,
                    user.status,
                    user.start_date,
                    user.end_date,
                    user.pay_rate,
                    user.nfc_id_code,
                )

                conn.query(query)

        self.execute_with_connection(inner_func)

    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, users_disable):
        # type: (List[str]) -> None

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

            query = """
                        UPDATE Users
                        SET Status= "1", TerminationDate='{}'
                        WHERE UserId IN ({})
                    """.format(datetime.now().date(), ",".join(users_disable))
            conn.query(query)

        return self.execute_with_connection(inner_func)

    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)
