# -*- coding: utf-8 -*-
import json
from datetime import datetime

from actions.models import GLAccount, TransferType, CloseOperator
from old_helper import BaseRepository


class AccountRepository(BaseRepository):
    def __init__(self, mb_context):
        super(AccountRepository, self).__init__(mb_context)

    def insert_close_operator_info(self, operator):
        # type: (CloseOperator) -> float

        def inner_func(conn):
            total_bordereau_difference = 0
            initial_fund = self.get_initial_amount(operator.pos_id, operator.session)
            total_cash_in = self.get_cash_in_total_amount(operator.pos_id, operator.session)
            total_cash_out = self.get_cash_out_total_amount(operator.pos_id, operator.session)
            final_cash = initial_fund + total_cash_in - total_cash_out
            for value in operator.values:
                amount = float(value["amount"])
                tender_id = int(value["tenderId"])
                has_payment = str(tender_id) in operator.payments
                sold_amount = 0.0 if not has_payment else operator.payments[str(tender_id)]
                envelope = _get_envelope(tender_id, operator)
                total_bordereau_difference += _get_total_difference(tender_id, amount, sold_amount, final_cash)

                gl_account = GLAccount(manager=operator.manager_id, envelope=envelope)
                gl_account_json = json.dumps(gl_account, default=lambda o: o.__dict__, sort_keys=True)

                conn.query("""INSERT INTO Transfer VALUES ('{}', {}, '{}', {}, '{}', {}, {}, '{}', '{}')"""
                           .format(operator.period, operator.pos_id, operator.session, TransferType.DECLARED_AMOUNT,
                                   "DECLARED_AMOUNT", amount, tender_id,
                                   datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S"), gl_account_json))

                conn.query("""INSERT INTO Transfer VALUES ('{}', {}, '{}', {}, '{}', {}, {}, '{}', '{}')"""
                           .format(operator.period, operator.pos_id, operator.session, TransferType.TRANSFER_SKIM,
                                   "TRANSFER_SKIM", sold_amount, tender_id,
                                   datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S"), ""))

            return total_bordereau_difference

        return self.execute_with_transaction(inner_func, db_name=str(operator.pos_id))

    def update_justification(self, pos_id, session, justification):
        def inner_func(conn):
            conn.query("""UPDATE Transfer
                          SET GLAccount = REPLACE(GLAccount, '"justification": ""', '"justification": "{}"')
                          WHERE SessionId = '{}' AND GLAccount IS NOT NULL AND GLAccount != '' AND Type IN (5,6)"""
                       .format(justification, session))

        self.execute_with_transaction(inner_func, db_name=str(pos_id))

    def get_closing_operator_sessions(self, pos_id, user, start_date, end_date):
        def inner_func(conn):
            sessions = []
            query = """SELECT SessionId
                       FROM Transfer
                       WHERE SessionId LIKE '%user={},%' and Period >= '{}' and Period <= '{}'
                       GROUP BY SessionId
                       ORDER BY Timestamp""".format(user, start_date, end_date)

            cursor = conn.select(query)
            if cursor.rows() > 0:
                for row in cursor:
                    sessions.append(row.get_entry("SessionId"))

            return sessions

        return self.execute_with_connection(inner_func, db_name=str(pos_id))

    def get_initial_amount(self, pos_id, session_id):
        return self.get_transfer_total_amount(pos_id, session_id, TransferType.INITIAL_AMOUNT)

    def get_cash_in_total_amount(self, pos_id, session_id):
        return self.get_transfer_total_amount(pos_id, session_id, TransferType.TRANSFER_CASH_IN)

    def get_cash_out_total_amount(self, pos_id, session_id):
        return self.get_transfer_total_amount(pos_id, session_id, TransferType.TRANSFER_CASH_OUT)

    def get_transfer_total_amount(self, pos_id, session_id, transfer_type):
        def inner_func(conn):
            query = """
                        SELECT amount FROM transfer
                        WHERE Type = '{}' AND SessionId = '{}'
                    """.format(transfer_type, session_id)
            cursor = conn.select(query)
            if cursor.rows() > 0:
                total_cash_in = 0
                for row in cursor:
                    total_cash_in += float(row.get_entry(0) or 0.0)

                return total_cash_in

            return 0

        return self.execute_with_connection(inner_func, db_name=str(pos_id))


def _get_envelope(tender_id, operator):
    if tender_id == 0:
        return operator.envelope
    return ""


def _get_total_difference(tender_id, amount, sold_amount, final_cash):
    if tender_id == 0:
        return (amount - sold_amount) - final_cash

    return amount - sold_amount
