# -*- coding: utf-8 -*-
import json
from datetime import datetime, date
from typing import List, Union, Dict
from xml.etree import cElementTree as eTree

from application.configurator import Configurator
from application.model import EventData
from iso8601 import parse_date


class ProductionOrderInteractor(object):

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

        self.configs = configurations
        self.production_order = None

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

        self.production_order = event_data.data.find("ProductionOrder") or event_data.data

        payload = {
            "created-at": self.production_order.get("created_at_gmt") + "Z",
            "operator-id": int(self.production_order.get("operator_id")),
            "operator-level": int(self.production_order.get("operator_level")),
            "operator-name": self.production_order.get("operator_name"),
            "order-id": int(self.production_order.get("order_id")),
            "originator": self.production_order.get("originator"),
            "pod-type": self.production_order.get("pod_type"),
            "pos-id": int(self.production_order.get("pos_id")),
            "prod-state": self.production_order.get("prod_state"),
            "sale-type": self.production_order.get("sale_type"),
            "session-id": self.production_order.get("session_id"),
            "state": self.production_order.get("state"),
            "state-id": int(self.production_order.get("state_id")),
            "store-code": str(self.configs.store_id),
            "store-division": str(self.configs.store_division) if self.configs.store_division else None,
            "total-gross": float(self.production_order.get("total_gross")),
            "tagged-lines": self._get_tagged_lines(),
            "items": self._get_items(),
            "properties": self._get_properties(),
            "state-history": self._get_state_history(),
            "prod-state-history": self._get_prod_state_history()
        }

        return json.dumps(payload)

    def _get_tagged_lines(self):
        # type: () -> Dict

        parsed_tagged_lines = self.production_order.get("tagged_lines").split(",")
        tagged_lines = {}

        for tagged_line in parsed_tagged_lines:
            items_tags = tagged_line.split("=")
            if len(items_tags) < 2:
                self.configs.logger.warning("tagged_line with less than two parameters: {}".format(tagged_line))
                continue

            item, tag = items_tags
            if item not in tagged_lines:
                tagged_lines[item] = [tag]
            else:
                tagged_lines[item].append(tag)

        return tagged_lines

    def _get_items(self):
        # type: () -> List

        properties = []

        for item in self.production_order.find("Items"):
            data = self._build_item_payload(item)
            properties.append(data)

        return properties

    def _build_item_payload(self, item):
        # type: (eTree) -> Dict

        data = {
            "context-id": item.get("item_code"),
            "default-qty": float(item.get("default_qty")),
            "description": item.get("description"),
            "item-type": item.get("item_type"),
            "level": int(item.get("level")),
            "line-number": int(item.get("line_number")),
            "max-qty": float(item.get("max_qty")),
            "min-qty": float(item.get("min_qty")),
            "multiplied-qty": float(item.get("multiplied_qty")),
            "part-code": int(item.get("part_code")),
            "product-priority": int(item.get("product_priority")),
            "qty": float(item.get("qty")),
            "qty-added": float(item.get("qty_added")),
            "qty-modified": float(item.get("qty_modified")),
            "qty-voided": float(item.get("qty_voided")),
            "product-tags": self._build_item_json_tags(item),
            "tags": self._build_item_tags(item),
            "tag-history": self._build_item_tag_history(item),
            "jit-lines": item.get("jit_lines"),
            "items": [self._build_item_payload(sub_item) for sub_item in item.findall("Item")]
        }

        return data

    @staticmethod
    def _build_item_json_tags(item):
        # type: (eTree) -> Dict

        json_tags = json.loads(item.get("json_tags") or "{}")
        tags = {}

        for tag in json_tags:
            key, value = tag.split("=")
            tags[str(key)] = str(value)

        return tags

    def _build_item_tags(self, item):
        # type: (eTree) -> List

        tags = []

        for tag in self._find(item, "Tags"):
            tags.append(str(tag.get("name")))

        return tags

    def _build_item_tag_history(self, item):
        # type: (eTree) -> List

        tags = []

        for tag_event in self._find(item, "TagHistory"):
            data = {
                "tag": tag_event.get("tag"),
                "action": tag_event.get("action"),
                "timestamp": self._parse_timestamp(tag_event.get("timestamp"))
            }
            tags.append(data)

        return tags

    def _get_properties(self):
        # type: () -> Dict

        properties = {}

        for prop in self._find(self.production_order, "Properties"):
            properties[prop.get("key")] = prop.get("value")

        return properties

    def _get_state_history(self):
        # type: () -> List

        state_history = []

        for prop in self._find(self.production_order, "StateHistory"):
            data = {
                "state": prop.get("state"),
                "state-id": int(prop.get("state_id")),
                "timestamp": self._parse_timestamp(prop.get("timestamp"))
            }
            state_history.append(data)

        return state_history

    def _get_prod_state_history(self):
        # type: () -> List

        prod_state_history = []

        for prop in self._find(self.production_order, "ProdStateHistory"):
            prod_state = prop.get("prod_state")
            if prod_state is None:
                continue

            data = {
                "prod-state": prod_state,
                "timestamp": self._parse_timestamp(prop.get("timestamp"))
            }
            prod_state_history.append(data)

        return prod_state_history

    @staticmethod
    def _find(element, prop):
        # type: (eTree, str) -> Union[eTree, List]

        return element.find(prop) or []

    def _parse_timestamp(self, timestamp):
        # type: (str) -> str

        parsed_timestamp = parse_date(timestamp).replace(tzinfo=None, microsecond=0)
        converted_time = self._convert_to_utc(parsed_timestamp)

        return converted_time.isoformat()

    @staticmethod
    def _convert_to_utc(local_datetime):
        # type: (date) -> date

        time_diff = datetime.utcnow() - datetime.now()
        converted_time = local_datetime + time_diff

        return converted_time
