from datetime import datetime, timedelta
from xml.etree import cElementTree as eTree

import iso8601
from application.model import (
    BenefitControllerDto,
    OperationStatus,
    BenefitAppliers,
    OperationDescription,
)
from application.model.configuration import Configurations
from mbcontextmessagehandler import MbContextMessageBus
from mwhelper import BaseRepository
from persistence import Connection
from typing import List, Optional


class DBRepository(BaseRepository):

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

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

        self.logger = configs.logger
        self.configs = configs
        self.message_bus = message_bus

    def manage_operation_status(
        self,                       # type: DBRepository
        success,                    # type: bool
        benefit_id,                 # type: str
        operation_description,      # type: OperationDescription
        benefit_applier,            # type: str
        retry_quantity,             # type: int
        order_picture=None,         # type: Optional[eTree]
        table_picture=None,         # type: Optional[eTree]
    ):
        # type: (...) -> None

        if success:
            self.delete_pending_operation(benefit_id)
        else:
            operation_status = OperationStatus.NOT_DONE

            retry_quantity += 1
            if retry_quantity > self.configs.max_retry_quantity:
                operation_status = OperationStatus.ERROR
                message = "The benefit has exceeded the max retry quantity. CouponId: {}; RetryQuantity: {}"
                self.logger.info(message.format(benefit_id, retry_quantity))

            self.insert_or_update_pending_operation(
                benefit_id=benefit_id,
                operation_description=operation_description,
                operation_status=operation_status,
                benefit_applier=benefit_applier,
                retry_quantity=retry_quantity,
                order_picture=order_picture,
                table_picture=table_picture,
            )

    def find_pending_operations(
        self,
    ):
        # type: () -> Optional[List[BenefitControllerDto]]
        def inner_func(conn):
            # type: (Connection) -> Optional[List[BenefitControllerDto]]

            query = """
                SELECT * 
                FROM PendingOperations 
                WHERE OperationStatusId = '{}'
            """.format(OperationStatus.NOT_DONE)

            cursor = conn.select(query)
            if not cursor.rows():
                return

            results = []
            for result in cursor:
                benefit = BenefitControllerDto(
                    benefit_id=result.get_entry("BenefitId"),
                    operation_description=int(result.get_entry("OperationDescriptionId")),
                    benefit_applier=result.get_entry("BenefitApplier"),
                    retry_quantity=int(result.get_entry("RetryQuantity")),
                    last_retry_date_utc=iso8601.parse_date(result.get_entry("LastRetryDateUTC")),
                    order_picture=result.get_entry("OrderPicture"),
                    table_picture=result.get_entry("TablePicture"),
                )

                results.append(benefit)

            return results

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

    def get_benefit_applier_by_db(self, benefit_id):
        # type: (str) -> str

        def inner_func(conn):
            # type: (Connection) -> Optional[BenefitAppliers]

            query = """
                SELECT BenefitApplier 
                FROM PendingOperations 
                WHERE BenefitId = '{0}'
            """.format(benefit_id)

            cursor = conn.select(query)
            if not cursor.rows():
                return

            return cursor.get_row(0).get_entry(0)

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

    def insert_or_update_pending_operation(
        self,                       # type: str
        benefit_id,                 # type: str
        operation_description,      # type: OperationDescription
        operation_status,           # type: OperationStatus
        benefit_applier,            # type: BenefitAppliers
        retry_quantity,             # type: int
        order_picture=None,         # type: Optional[eTree]
        table_picture=None,         # type: Optional[eTree]
    ):
        # type: (...) -> None

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

            last_retry_date = datetime.utcnow().isoformat()

            query = """
                INSERT OR REPLACE INTO PendingOperations(
                    BenefitId,
                    OrderPicture,
                    TablePicture,
                    OperationDescriptionId,
                    OperationStatusId,
                    BenefitApplier,
                    RetryQuantity,
                    LastRetryDateUTC
                )
                VALUES ('{0}', {1}, {2}, '{3}', '{4}', '{5}', '{6}', '{7}')
            """
            query = query.format(
                benefit_id,
                "'{}'".format(eTree.tostring(order_picture)) if order_picture else "NULL",
                "'{}'".format(eTree.tostring(table_picture)) if table_picture else "NULL",
                operation_description,
                operation_status,
                benefit_applier,
                retry_quantity,
                last_retry_date,
            )
            conn.query(query)

        self.execute_with_transaction(inner_func, service_name=self.configs.persistence_name)

    def change_benefit_operation_status_to_error(
        self,                       # type: str
        benefit_id,                 # type: str
    ):
        # type: (...) -> None

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

            query = """
                UPDATE PendingOperations
                SET OperationStatusId = {}
                WHERE BenefitId = {}
            """.format(
                OperationStatus.ERROR,
                benefit_id
            )
            conn.query(query)

        self.execute_with_transaction(inner_func, service_name=self.configs.persistence_name)

    def delete_pending_operation(
        self,            # type: DBRepository
        benefit_id,      # type: str
    ):
        # type: (str) -> None

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

            query = """
                DELETE FROM PendingOperations 
                WHERE BenefitId = '{0}'
            """.format(benefit_id)
            conn.query(query)

        self.execute_with_transaction(inner_func, service_name=self.configs.persistence_name)
