from datetime import datetime
from typing import (
    Dict,
    List,
    Optional,
)

from xml.etree import cElementTree as eTree

from ._OrderFormatter import OrderFormatter
from ._Type import JSON
from ._util import get_tender_detail


class TableFormatter:

    def __init__(self):
        return

    def get_table_picture_dict(
        self,
        table_picture,          # type: eTree.Element
    ):
        # type: (...) -> JSON

        start_ts = table_picture.get("startTS")
        start_ts = self._get_time_stamp(start_ts)

        finish_ts = table_picture.get("finishTS")
        finish_ts = self._get_time_stamp(finish_ts)

        linked_tables = table_picture.get("linkedTables")
        linked_tables = linked_tables.split(",") if linked_tables is not None else []

        table_picture_dict = {
            "id": table_picture.get("id"),
            "businessPeriod": table_picture.get("businessPeriod"),
            "calculatedTip": float(table_picture.get("calculatedTip", 0)),
            "lastUpdateTS": table_picture.get("lastUpdateTS"),
            "name": table_picture.get("name"),
            "seats": int(table_picture.get("seats", 1)),
            "sector": table_picture.get("sector"),
            "serviceId": int(table_picture.get("serviceId", 0)) or None,
            "serviceType": table_picture.get("type"),
            "serviceTypeDesc": table_picture.get("typeDescr"),
            "serviceSeats": int(table_picture.get("serviceSeats", 1)),
            "startTS": start_ts,
            "finishTS": finish_ts,
            "status": table_picture.get("status"),
            "statusDesc": table_picture.get("statusDescr"),
            "sliceSourceTableId": table_picture.get("sliceSourceTableId", ""),
            "sliceParentTableId": table_picture.get("sliceParentTableId", ""),
            "sliceSourceServiceId": self._get_source_service_id(table_picture),
            "subSector": table_picture.get("subSector"),
            "userId": int(table_picture.get("userId", 0)) or None,
            "linkedTables": linked_tables,
            "totalAmount": float(table_picture.get("totalAmount", 0)),
            "discountAmount": float(table_picture.get("discountAmount", 0)),
            "tipAmount": float(table_picture.get("tipAmount")) if table_picture.get("tipAmount") else 0,
            "tipRate": float(table_picture.get("tipRate")) if table_picture.get("tipRate") else 0,
            "totalTender": float(table_picture.get("totalTender", 0)),
            "dueAmount": float(table_picture.get("dueAmount", 0)),
            "change": float(table_picture.get("change", 0)),
            "taxTotal": float(table_picture.get("taxTotal", 0)),
            "ServiceChildren": [],
            "Properties": self._get_custom_properties(table_picture),
            "ServiceOrders": self._get_orders(table_picture, "ServiceOrders/ServiceOrder"),
            "PaymentOrders": self._get_orders(table_picture, "PaymentOrders/PaymentOrder"),
            "ServiceTenders": self._get_service_tenders(table_picture),
            "Orders": self._get_orders_info(table_picture),
        }

        current_pos_id = table_picture.get("currentPOSId")
        if current_pos_id is not None:
            table_picture_dict["currentPOSId"] = current_pos_id

        return table_picture_dict

    @staticmethod
    def _get_time_stamp(time_stamp):
        if time_stamp:
            try:
                ret_time_stamp = datetime.strptime((time_stamp + "Z"), "%Y-%m-%d %H:%M:%SZ")
            except ValueError:
                try:
                    ret_time_stamp = datetime.strptime((time_stamp + "Z"), "%Y-%m-%dT%H:%M:%SZ")
                except ValueError:
                    return None
            return datetime.strftime(ret_time_stamp, "%Y-%m-%dT%H:%M:%SZ")

        return None

    @staticmethod
    def _get_custom_properties(
        table_picture           # type: eTree.Element
    ):
        # type: (...) -> Dict

        payload_custom_properties = {}
        custom_properties = table_picture.find("Properties")
        if custom_properties is not None:
            for key, value in custom_properties.items():
                payload_custom_properties[key] = value

        return payload_custom_properties

    @staticmethod
    def _get_orders(
        table_picture,      # type: eTree.Element
        tag                 # type: str
    ):
        # type: (...) -> List

        payload_orders = []
        for order_line in table_picture.findall("{}".format(tag)):
            order_id = int(order_line.get("orderId", -1))
            order_state = order_line.get("state")
            created_at = order_line.get("createdAt")
            if created_at is None:
                created_at = datetime.utcfromtimestamp(0).isoformat()

            order_payload = {
                "orderId": order_id,
                "state": order_state,
                "createdAt": created_at + "Z"
            }
            payload_orders.append(order_payload)

        return payload_orders

    @staticmethod
    def _get_orders_info(
        table_picture,  # type: eTree.Element
    ):
        # type: (...) -> List

        table_orders = table_picture.find("Orders")
        if not table_orders:
            return []

        orders = []
        order_formatter = OrderFormatter()
        for order in table_orders.findall("Order"):
            orders.append(order_formatter.get_order_picture_dict(order_picture=order))

        return orders

    @staticmethod
    def _get_source_service_id(
        table_picture       # type: eTree.Element
    ):
        # type: (...) -> Optional[int]

        if table_picture.get("sourceServiceId") is not None:
            return int(table_picture.get("sourceServiceId"))

        return None

    @staticmethod
    def _get_service_tenders(
        table_picture       # type: eTree.Element
    ):
        # type: (...) -> List

        service_tenders = []
        for tender_line in table_picture.findall("ServiceTenders/ServiceTender"):
            tender_payload = {
                "serviceTenderId": tender_line.get("serviceTenderId"),
                "orderId": tender_line.get("orderId"),
                "orderTenderId": tender_line.get("orderTenderId"),
                "tenderAmount": float(tender_line.get("tenderAmount", 0)),
                "tenderDescr": tender_line.get("TenderDescr"),
                "tenderType": tender_line.get("tenderId"),
                "changeAmount": float(tender_line.get("change", 0)),
                "tenderDetail": get_tender_detail(tender_line=tender_line),
                "tenderTip": float(tender_line.get("tip", 0))
            }
            service_tenders.append(tender_payload)

        return service_tenders
