# -*- coding: utf-8 -*-

from datetime import datetime

from application.model import UpdateType, UpdateStatus
from application.model.configuration import Configurations
from application.model.customexception import ErrorInsertingNewUpdateVersion, ErrorRestartingHV, ErrorGettingUsersList,\
    ErrorUpdatingUserControlType
from mbcontextmessagehandler import MbContextMessageBus
from msgbus import (
    TK_SYS_ACK,
    TK_POS_LISTUSERS,
    TK_POS_GETMODEL
)
from messagebus import Message, DataType
from mwhelper import (
    BaseRepository,
    restart_system,
)
from persistence import Connection
from typing import (
    Any,
    Optional,
    Union
)
from xml.etree import cElementTree as etree

from ._CommonPOSRepository import CommonPOSRepository


class LoaderUpdaterPOSRepository(BaseRepository):
    def __init__(self, message_bus, configs):
        # type: (MbContextMessageBus, Configurations) -> None

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

        self.message_bus = message_bus
        self.configs = configs
        self.update_type = UpdateType.loader
        self.update_value = self.update_type.value
        self.local_configs = configs.updaters[self.update_type.name]
        self.common_pos_repository = CommonPOSRepository(message_bus.mbcontext)

    def insert_new_loader_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)
                VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', '{4}', '{4}')"""
                query_format = query.format(update_id, update_name, self.update_value, UpdateStatus.pending,
                                            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_loader_update_id(self):
        # type: () -> int

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

            query = """
                SELECT UpdateId
                FROM UpdatesController
                WHERE TypeId = '{}'
                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 update_pos_ctrl_type(self, pos_id, user_control_type):
        # type: (int, int) -> None

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

                query = """
                    UPDATE PosState
                    SET UserControlType = '{0}'
                    WHERE PosId = '{1}'""".format(user_control_type, pos_id)
                conn.query(query)

            self.execute_with_connection(inner_func, service_name='Persistence')
        except Exception as ex:
            raise ErrorUpdatingUserControlType(ex)

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

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

            query = """
                SELECT UpdateId
                FROM UpdatesController
                WHERE TypeId = '{0}' AND StatusId = '{1}' AND AppliedDate IS NULL
                ORDER BY Id DESC""".format(self.update_value, UpdateStatus.pending)

            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 mark_loader_update_as_applied(
            self,
    ):
        # type: (...) -> None

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

                date_now = str(datetime.now().isoformat())
                query = """
                        UPDATE UpdatesController
                        SET StatusId = '{0}', AppliedDate = '{1}', NotifiedDate = '{1}'
                        WHERE UpdateName = 'Loaders' AND AppliedDate IS NULL
                        """.format(UpdateStatus.applied, date_now)

                conn.query(query)

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

    def mark_loader_update_as_error(
            self,
    ):
        # type: (...) -> None

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

                query = """
                        UPDATE UpdatesController
                        SET StatusId = '{0}'
                        WHERE UpdateName = 'Loaders' AND AppliedDate IS NULL
                        """.format(UpdateStatus.error)

                conn.query(query)

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

    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 get_last_loader_update_date(self):
        # type: () -> Optional[datetime]

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

    def restart_hv(self):
        # type: () -> None
        res = restart_system(
            mb_context=self.message_bus.mbcontext,
            event_type="POS_UPDATER_LOADER",
        )
        if res.token != TK_SYS_ACK:
            raise ErrorRestartingHV("Could not perform HV global restart")

    def get_pos_users(self, pos_id):
        # type: (int) -> str

        message = Message(TK_POS_LISTUSERS, data_type=DataType.param, data="{}".format(pos_id))
        res = self.message_bus.send_message("POS{}".format(pos_id), message)
        if res.token != TK_SYS_ACK:
            raise ErrorGettingUsersList("Could not list users in POS{}".format(pos_id))
        return res.data

    def get_model(
        self,
        pos_id    # type: Any[Union[str, int]]
    ):
        # type: (...) -> etree.XML

        message = Message(TK_POS_GETMODEL, data_type=DataType.param, data="{}".format(pos_id))
        res = self.message_bus.send_message("POS{}".format(pos_id), message)
        if res.token != TK_SYS_ACK:
            raise ErrorGettingUsersList("Could not list users in POS{}".format(pos_id))
        return etree.XML(res.data)
