import json
from logging import Logger
from xml.etree import cElementTree as eTree

from application.model import BenefitAppliers
from application.model.repository.benefit_applier.exception import BenefitApplierNotFound
from application.model.repository.benefit_applier import DefaultBenefitApplierRepository
from typing import Optional, Dict, List

BENEFIT_APPLIERS_REPOSITORIES = None  # type: Optional[Dict[BenefitAppliers: DefaultBenefitApplierRepository]]


def find_benefit_applier(
    pos_id,     # type: int
    coupon_id,  # type: str
):
    # type: (...) -> BenefitAppliers

    for benefit_applier in sorted(BENEFIT_APPLIERS_REPOSITORIES, reverse=True):
        if BENEFIT_APPLIERS_REPOSITORIES[benefit_applier].check_coupon_id(pos_id, coupon_id):
            return benefit_applier

    raise BenefitApplierNotFound("NDISCOUNT_BENEFIT_NOT_FOUND")


def find_benefit_id_by_custom_property(order_picture):
    # type: (eTree) -> Optional[str]

    for benefit_applier in BENEFIT_APPLIERS_REPOSITORIES:
        benefit_id = BENEFIT_APPLIERS_REPOSITORIES[benefit_applier].get_benefit_id_by_custom_property(order_picture)
        if benefit_id:
            return benefit_applier, benefit_id


def find_order_benefits_id(logger, order_picture, validate_voided_lines=False):
    # type: (Logger, eTree, Optional[bool]) -> Optional[List[str]]

    order_id = order_picture.get("orderId")
    logger.info("Finding order benefits to orderId: {}".format(order_id))

    order_benefits_id = []
    try:
        benefit_prop = order_picture.find(".//OrderProperty[@key='BENEFIT_LIST']")
        if benefit_prop is None:
            return order_benefits_id

        voided_sale_lines = _get_removed_sale_lines(order_picture)

        applied_benefits = json.loads(benefit_prop.get("value", ""))
        benefit_list = json.loads(applied_benefits.get("benefitList"))

        for applied_benefit in benefit_list:
            if validate_voided_lines:
                added_sale_lines = applied_benefit.get("added_sale_lines")
                has_voided_sale_lines = [sale_line for sale_line in added_sale_lines if sale_line in voided_sale_lines]

                if not has_voided_sale_lines:
                    continue

            benefit = applied_benefit.get("benefit")
            if benefit:
                benefit_id = benefit.get("id")
            else:
                continue

            if benefit_id not in order_benefits_id:
                order_benefits_id.append(benefit_id)

        return order_benefits_id
    finally:
        logger.info("Found [{}] benefits on orderId: {}".format(len(order_benefits_id), order_id))


def _get_removed_sale_lines(order_picture):
    # type: (eTree) -> List

    sale_lines = order_picture.findall(".//SaleLine")
    removed_sale_lines = []
    for sale_line in sale_lines:
        if sale_line.get("level") == "0" and sale_line.get("qty") == "0":
            removed_sale_lines.append(int(sale_line.get("lineNumber")))

    return removed_sale_lines


def fill_benefit_appliers_repositories(benefit_appliers_repositories):
    # type: (Dict[BenefitAppliers: DefaultBenefitApplierRepository]) -> None

    global BENEFIT_APPLIERS_REPOSITORIES

    BENEFIT_APPLIERS_REPOSITORIES = benefit_appliers_repositories


def get_originator_pos_id(order_picture):
    # type: (eTree) -> int

    originator_pos_id = order_picture.get("originatorId")
    if originator_pos_id is not None:
        return int(originator_pos_id.split("POS")[-1])
