from datetime import (
    date,
    datetime,
    UTC,
)
from typing import (
    Dict,
    List,
    Optional,
    Tuple,
)

import iso8601
from ._FiscalData import FiscalData
from ._OrderState import OrderState
from ._SaleLine import SaleLine
from ._SaleType import SaleType
from ._UpdatableList import UpdatableList
from ._Tender import Tender


class OrderPicture(object):
    def __init__(
        self,
        order_picture_id: int,
        store_code: str,
        store_cnpj: str,
        order_code: int,
        creation_dttm: datetime,
        business_date: date,
        pos_code: int,
        pod_type: str,
        state: OrderState,
        sale_type: SaleType,
        total_gross: float,
        tax_total: float,
        discount_amount: float,
        tenders: List[Tender],
        sale_lines: List[SaleLine],
        fiscal_data: Optional[FiscalData],
        custom_properties: Dict[str, str],
    ):
        self.order_picture_id = order_picture_id
        self.store_code = store_code
        self.store_cnpj = store_cnpj
        self.order_code = order_code
        self.creation_dttm = creation_dttm
        self.business_date = business_date
        self.pos_code = pos_code
        self.pod_type = pod_type
        self.state = state
        self.sale_type = sale_type
        self.total_gross = total_gross
        self.tax_total = tax_total
        self.discount_amount = discount_amount
        self._tenders_by_id = UpdatableList(tenders)
        self._sale_lines_by_id = UpdatableList(sale_lines)
        self.fiscal_data = fiscal_data
        self.custom_properties = custom_properties
        self.created_at = datetime.now(tz=UTC)
        self.updated_at = None

        self._sale_line_trees = None

    @classmethod
    def create_from_dict(cls, record: dict):
        order_picture = cls(
            order_picture_id=record["id"],
            store_code=record["storeCode"],
            store_cnpj=record["storeCNPJ"],
            order_code=record["orderCode"],
            creation_dttm=iso8601.parse_date(record["creationDttm"]),
            business_date=iso8601.parse_date(record["businessDt"]).date(),
            state=OrderState(record["stateId"]),
            pos_code=record["posCode"],
            pod_type=record["podType"],
            sale_type=SaleType(record["saleTypeId"]),
            total_gross=record["totalGross"],
            tax_total=record["taxTotal"],
            discount_amount=record["discountAmount"],
            tenders=Tender.create_list_from_list(records=record["tenders"]),
            sale_lines=SaleLine.create_list_from_list(records=record["saleLines"]),
            fiscal_data=FiscalData.create_from_dict(record),
            custom_properties=record["customProperties"],
        )
        return order_picture

    def update(self, new_data: 'OrderPicture'):
        self.order_code = new_data.order_code
        self.store_code = new_data.store_code
        self.store_cnpj = new_data.store_cnpj
        self.creation_dttm = new_data.creation_dttm
        self.business_date = new_data.business_date
        self.pod_type = new_data.pod_type
        self.pos_code = new_data.pos_code
        self.state = new_data.state
        self.sale_type = new_data.sale_type
        self.total_gross = new_data.total_gross
        self.tax_total = new_data.tax_total
        self.discount_amount = new_data.discount_amount
        if new_data.fiscal_data is not None:
            if self.fiscal_data is None:
                self.fiscal_data = new_data.fiscal_data
            else:
                self.fiscal_data.update(new_data.fiscal_data)
        else:
            self.fiscal_data = None
        self.custom_properties = new_data.custom_properties
        self.updated_at = datetime.now(tz=UTC)

        self._tenders_by_id.update(new_data.tenders)
        self._sale_lines_by_id.update(new_data.sale_lines)


    @property
    def tenders(self) -> List[Tender]:
        return list(self._tenders_by_id.values())

    @property
    def sale_lines(self) -> List[SaleLine]:
        return list(self._sale_lines_by_id.values())

    @property
    def fiscal_date(self) -> Optional[datetime]:
        if self.fiscal_data is None or self.fiscal_data.fiscal_xml is None:
            return None

        return self.fiscal_data.fiscal_date

    @property
    def sale_line_trees(self) -> List[SaleLine]:
        if self._sale_line_trees is None:
            self._sale_line_trees = self._transform_sale_line_in_trees()

        return self._sale_line_trees

    @property
    def customer_name(self) -> Optional[str]:
        if "CUSTOMER_NAME" in self.custom_properties:
            return self.custom_properties["CUSTOMER_NAME"]

        return None

    @property
    def customer_doc(self) -> Optional[str]:
        if "CUSTOMER_DOC" in self.custom_properties:
            return self.custom_properties["CUSTOMER_DOC"]

        return None

    def _transform_sale_line_in_trees(self) -> List[SaleLine]:
        sale_line_trees = []
        sale_line_dict: Dict[Tuple[int, str], SaleLine] = {}
        for sale_line in self.sale_lines:
            sale_line_dict[(sale_line.line_number, sale_line.item_id)] = sale_line

        for sale_line in self.sale_lines:
            if sale_line.level == 0:
                sale_line_trees.append(sale_line)
            else:
                father_sale_line = sale_line_dict[(sale_line.line_number, sale_line.context)]
                father_sale_line.add_son(sale_line)

        return sale_line_trees
