# Copyright 2018 ACSONE SA/NV.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import itertools
from collections import namedtuple

from odoo.fields import Date

Criteria = namedtuple(
    "Criteria",
    [
        "when",  # Contract line relatively to today (BEFORE, IN, AFTER)
        "has_date_end",  # Is date_end set on contract line (bool)
        "has_last_date_invoiced",  # Is last_date_invoiced set on contract line
        "is_auto_renew",  # Is is_auto_renew set on contract line (bool)
        "has_successor",  # Is contract line has_successor (bool)
        "predecessor_has_successor",
        # Is contract line predecessor has successor (bool)
        # In almost of the cases
        # contract_line.predecessor.successor == contract_line
        # But at cancel action,
        # contract_line.predecessor.successor == False
        # This is to permit plan_successor on predecessor
        # If contract_line.predecessor.successor != False
        # and contract_line is canceled, we don't allow uncancel
        # else we re-link contract_line and its predecessor
        "canceled",  # Is contract line canceled (bool)
    ],
)
Allowed = namedtuple(
    "Allowed",
    ["plan_successor", "stop_plan_successor", "stop", "cancel", "uncancel"],
)


def _expand_none(criteria):
    variations = []
    for attribute, value in criteria._asdict().items():
        if value is None:
            if attribute == "when":
                variations.append(["BEFORE", "IN", "AFTER"])
            else:
                variations.append([True, False])
        else:
            variations.append([value])
    return itertools.product(*variations)


def _add(matrix, criteria, allowed):
    """Expand None values to True/False combination"""
    for c in _expand_none(criteria):
        matrix[c] = allowed


CRITERIA_ALLOWED_DICT = {
    Criteria(
        when="BEFORE",
        has_date_end=True,
        has_last_date_invoiced=False,
        is_auto_renew=True,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=True,
        stop=True,
        cancel=True,
        uncancel=False,
    ),
    Criteria(
        when="BEFORE",
        has_date_end=True,
        has_last_date_invoiced=False,
        is_auto_renew=False,
        has_successor=True,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=False,
        stop=True,
        cancel=True,
        uncancel=False,
    ),
    Criteria(
        when="BEFORE",
        has_date_end=True,
        has_last_date_invoiced=False,
        is_auto_renew=False,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=True,
        stop_plan_successor=True,
        stop=True,
        cancel=True,
        uncancel=False,
    ),
    Criteria(
        when="BEFORE",
        has_date_end=False,
        has_last_date_invoiced=False,
        is_auto_renew=False,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=True,
        stop=True,
        cancel=True,
        uncancel=False,
    ),
    Criteria(
        when="IN",
        has_date_end=True,
        has_last_date_invoiced=False,
        is_auto_renew=True,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=True,
        stop=True,
        cancel=True,
        uncancel=False,
    ),
    Criteria(
        when="IN",
        has_date_end=True,
        has_last_date_invoiced=False,
        is_auto_renew=False,
        has_successor=True,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=False,
        stop=True,
        cancel=True,
        uncancel=False,
    ),
    Criteria(
        when="IN",
        has_date_end=True,
        has_last_date_invoiced=False,
        is_auto_renew=False,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=True,
        stop_plan_successor=True,
        stop=True,
        cancel=True,
        uncancel=False,
    ),
    Criteria(
        when="IN",
        has_date_end=False,
        has_last_date_invoiced=False,
        is_auto_renew=False,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=True,
        stop=True,
        cancel=True,
        uncancel=False,
    ),
    Criteria(
        when="BEFORE",
        has_date_end=True,
        has_last_date_invoiced=True,
        is_auto_renew=True,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=True,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="BEFORE",
        has_date_end=True,
        has_last_date_invoiced=True,
        is_auto_renew=False,
        has_successor=True,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=False,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="BEFORE",
        has_date_end=True,
        has_last_date_invoiced=True,
        is_auto_renew=False,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=True,
        stop_plan_successor=True,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="BEFORE",
        has_date_end=False,
        has_last_date_invoiced=True,
        is_auto_renew=False,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=True,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="IN",
        has_date_end=True,
        has_last_date_invoiced=True,
        is_auto_renew=True,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=True,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="IN",
        has_date_end=True,
        has_last_date_invoiced=True,
        is_auto_renew=False,
        has_successor=True,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=False,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="IN",
        has_date_end=True,
        has_last_date_invoiced=True,
        is_auto_renew=False,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=True,
        stop_plan_successor=True,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="IN",
        has_date_end=False,
        has_last_date_invoiced=True,
        is_auto_renew=False,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=True,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="AFTER",
        has_date_end=True,
        has_last_date_invoiced=None,
        is_auto_renew=True,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=False,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="AFTER",
        has_date_end=True,
        has_last_date_invoiced=None,
        is_auto_renew=False,
        has_successor=True,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=False,
        stop=False,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when="AFTER",
        has_date_end=True,
        has_last_date_invoiced=None,
        is_auto_renew=False,
        has_successor=False,
        predecessor_has_successor=None,
        canceled=False,
    ): Allowed(
        plan_successor=True,
        stop_plan_successor=False,
        stop=True,
        cancel=False,
        uncancel=False,
    ),
    Criteria(
        when=None,
        has_date_end=None,
        has_last_date_invoiced=None,
        is_auto_renew=None,
        has_successor=None,
        predecessor_has_successor=False,
        canceled=True,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=False,
        stop=False,
        cancel=False,
        uncancel=True,
    ),
    Criteria(
        when=None,
        has_date_end=None,
        has_last_date_invoiced=None,
        is_auto_renew=None,
        has_successor=None,
        predecessor_has_successor=True,
        canceled=True,
    ): Allowed(
        plan_successor=False,
        stop_plan_successor=False,
        stop=False,
        cancel=False,
        uncancel=False,
    ),
}
criteria_allowed_dict = {}

for c in CRITERIA_ALLOWED_DICT:
    _add(criteria_allowed_dict, c, CRITERIA_ALLOWED_DICT[c])


def compute_when(date_start, date_end):
    today = Date.today()
    if today < date_start:
        return "BEFORE"
    if date_end and today > date_end:
        return "AFTER"
    return "IN"


def compute_criteria(
    date_start,
    date_end,
    has_last_date_invoiced,
    is_auto_renew,
    successor_contract_line_id,
    predecessor_contract_line_id,
    is_canceled,
):
    return Criteria(
        when=compute_when(date_start, date_end),
        has_date_end=bool(date_end),
        has_last_date_invoiced=bool(has_last_date_invoiced),
        is_auto_renew=is_auto_renew,
        has_successor=bool(successor_contract_line_id),
        predecessor_has_successor=bool(
            predecessor_contract_line_id.successor_contract_line_id
        ),
        canceled=is_canceled,
    )


def get_allowed(
    date_start,
    date_end,
    has_last_date_invoiced,
    is_auto_renew,
    successor_contract_line_id,
    predecessor_contract_line_id,
    is_canceled,
):
    criteria = compute_criteria(
        date_start,
        date_end,
        has_last_date_invoiced,
        is_auto_renew,
        successor_contract_line_id,
        predecessor_contract_line_id,
        is_canceled,
    )
    if criteria in criteria_allowed_dict:
        return criteria_allowed_dict[criteria]
    return False
