# -*- coding: utf-8 -*-
import json
import os

from datetime import datetime
from typing import List

import cfgtools
from old_helper import BaseRepository

from utilfunctions import (
    get_tender_info,
    is_electronic_payment,
)

from actions.models import GLAccount, TransferType, CloseOperator


class AccountRepository(BaseRepository):
    def __init__(self, mb_context):
        super(AccountRepository, self).__init__(mb_context)
        config = cfgtools.read(os.environ["LOADERCFG"])
        self.considers_tef = config.find_value("Customizations.ConsidersTEFOnBordereau", "false").lower() == "true"

    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

            operator_values = operator.values
            self._insert_tef_tender(
                operator=operator,
                operator_values=operator_values
            )

            for value in operator_values:
                tender_id = int(value["tenderId"])
                tender = get_tender_info(int(tender_id))
                tender_is_electronic = is_electronic_payment(tender)
                amount = float(value["amount"])
                if self.considers_tef:
                    amount = float(value["amount"]) if not tender_is_electronic else None

                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)

                new_amount = "{}".format('NULL') if amount is None else amount

                conn.query(
                    """
                        INSERT INTO Transfer 
                        VALUES ('{}', {}, '{}', {}, '{}', {}, {}, '{}', '{}')
                    """.format(
                        operator.period,
                        operator.pos_id,
                        operator.session,
                        TransferType.DECLARED_AMOUNT,
                        "DECLARED_AMOUNT",
                        new_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))

    @staticmethod
    def _insert_tef_tender(
        operator,           # type: CloseOperator
        operator_values,    # type: List
    ):
        # type: (...) -> None

        for tender_id, amount in operator.payments.iteritems():
            tender = get_tender_info(int(tender_id))
            if not is_electronic_payment(tender):
                continue

            operator_values.append({"tenderId": int(tender_id), u"amount": str(amount)})

    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 amount is None:
        amount = 0

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

    return amount - sold_amount
