# -*- coding: utf-8 -*-
from datetime import datetime
from typing import Optional, List

import simplejson as json
from application.configurator import Configurator
from application.model import EventData, TransferType
from application.repository import MessageBusRepository
from iso8601 import parse_date


class OperatorLogoutInteractor(object):

    def __init__(self, configurations, repository):
        # type: (Configurator, MessageBusRepository) -> None

        self.configs = configurations
        self.logger = self.configs.logger
        self.repository = repository

    def build(self, event_data):
        # type: (EventData) -> Optional[str]

        session_id = event_data.session_id
        session_user = self._get_user_from_session_id(session_id)
        if not session_id or session_user == "0":
            self.logger.error("Invalid 'sessionid' in the 'OPERATORLOGOUT' event. Dismissing it")
            return

        business_period = event_data.data.get("period")
        payload_period = datetime.strptime(business_period, "%Y%m%d")
        pos_id = event_data.data.get("posid")
        operator_time = self.repository.get_operator_time(session_id)
        op_open = operator_time.get("OpenTime")
        op_close = operator_time.get("CloseTime")

        payload = {
            "store-code": str(self.configs.store_id),
            "business-dt": payload_period.strftime("%Y-%m-%d"),
            "pos-code": pos_id,
            "pos-user-id": int(event_data.data.get("operatorid")),
            "session": session_id,
            "opened-at-dttm": op_open,
            "closed-at-dttm": op_close,
            "closed-drawer-detail": self._get_closed_drawer_detail(pos_id, session_id),
            "paid-in-out-list": self._get_paid_in_out_list(event_data),
            "justification": self._get_closed_drawer_justification(
                pos_id=pos_id,
                session_id=session_id,
            )
        }

        return json.dumps(payload)

    def _get_paid_in_out_list(self, event_data):
        # type: (EventData) -> List

        paid_in_out_list = []
        session_id = event_data.data.get("sessionid")

        transfers = self.repository.get_cash_in_out_by_session_id(session_id)
        for transfer in transfers:
            data = {
                "transaction-type": transfer.get("description"),
                "transaction-amount": transfer.get("amount"),
                "transaction-dttm": parse_date(transfer.get("timestamp")).isoformat(),
                "tender-type": transfer.get("tender_type"),
                "authorized-by": int(transfer.get("manager_id")) if transfer.get("manager_id") else None
            }
            paid_in_out_list.append(data)

        return paid_in_out_list

    def _get_closed_drawer_detail(self, pos_id, session_id):
        # type: (str, str) -> List

        payload = []
        transfers_by_session_id = self.repository.get_transfers_by_session_id(pos_id, session_id)
        transfer_order_info = self.repository.get_transfer_order_info(session_id)
        for tender_id, transfer in transfers_by_session_id.iteritems():
            total_tender = 0
            tip_amount = 0
            payment_quantity = 0
            if tender_id in transfer_order_info:
                order_info = transfer_order_info[tender_id]
                total_tender = order_info.total_tender
                tip_amount = order_info.tip_amount
                payment_quantity = order_info.payment_quantity

            payload.append(
                {
                    "tender-type-code": str(tender_id),
                    "computed-amount": total_tender,
                    "tip-amount": tip_amount,
                    "transaction-count": payment_quantity,
                    "initial-float-amount": self._get_transfer_amount(transfer, TransferType.INITIAL_AMOUNT.value),
                    "paid-in-amount": self._get_transfer_amount(transfer, TransferType.TRANSFER_CASH_IN.value),
                    "paid-out-amount": self._get_transfer_amount(transfer, TransferType.TRANSFER_CASH_OUT.value),
                    "skim-amount":self._get_transfer_amount(transfer, TransferType.TRANSFER_SKIM.value),
                    "counted-amount": self._get_transfer_amount(transfer, TransferType.DECLARED_AMOUNT.value)
                }
            )

        return payload

    def _get_closed_drawer_justification(
        self,
        pos_id,         # type: str
        session_id,     # type: str
    ):
        # type: (...) -> str

        return self.repository.get_transfer_justification(
            pos_id=pos_id,
            session_id=session_id,
            transfer_description="FINAL_AMOUNT",
        )

    @staticmethod
    def _get_transfer_amount(transfer, transfer_type):
        if transfer_type in transfer:
            return transfer[transfer_type]

        return 0

    @staticmethod
    def _get_user_from_session_id(session_id):
        try:
            session_user = None
            split_session_user = session_id.split("user=")
            if len(split_session_user) > 1:
                session_user = split_session_user[1].split(",")[0]
            return session_user
        except Exception as _:
            return None
