# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import functools

from trytond.i18n import gettext
from trytond.model import ModelView, Workflow, fields
from trytond.model.exceptions import AccessError
from trytond.pool import PoolMeta
from trytond.pyson import Bool, Eval, If
from trytond.transaction import Transaction


def no_payment(error):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(cls, sales, *args, **kwargs):
            for sale in sales:
                if not all((p.state == 'failed' for p in sale.payments)):
                    raise AccessError(gettext(error, sale=sale.rec_name))
            return func(cls, sales, *args, **kwargs)
        return wrapper
    return decorator


class Sale(metaclass=PoolMeta):
    __name__ = 'sale.sale'
    payments = fields.One2Many(
        'account.payment', 'origin', "Payments",
        domain=[
            ('company', '=', Eval('company', -1)),
            ['OR',
                ('party', '=', If(Bool(Eval('invoice_party')),
                        Eval('invoice_party', -1), Eval('party', -1))),
                ('state', '!=', 'draft'),
                ],
            ('currency', '=', Eval('currency', -1)),
            ],
        states={
            'readonly': Eval('state') != 'quotation',
            })

    @classmethod
    @ModelView.button
    @Workflow.transition('cancelled')
    @no_payment('sale_payment.msg_sale_cancel_payment')
    def cancel(cls, sales):
        super().cancel(sales)

    @classmethod
    @ModelView.button
    @Workflow.transition('draft')
    @no_payment('sale_payment.msg_sale_draft_payment')
    def draft(cls, sales):
        super().draft(sales)

    @classmethod
    def copy(cls, sales, default=None):
        if default is None:
            default = {}
        else:
            default = default.copy()
        default.setdefault('payments', None)
        return super().copy(sales, default=default)

    @property
    def payment_amount_authorized(self):
        "Total amount of the authorized payments"
        return (sum(
                p.amount for p in self.payments
                if p.kind == 'receivable' and p.is_authorized)
            - sum(p.amount for p in self.payments if p.kind == 'payable'))

    @property
    def amount_to_pay(self):
        "Amount to pay to confirm the sale"
        return self.total_amount

    @classmethod
    def payment_confirm(cls, sales=None):
        "Confirm the sale based on payment authorization"
        if sales is None:
            context = Transaction().context
            sales = cls.search([
                    ('state', '=', 'quotation'),
                    ('payments', '!=', None),
                    ('company', '=', context.get('company')),
                    ])

        def cover(authorized, amount):
            return (
                abs(authorized) >= abs(amount)
                and (authorized * amount >= 0))

        to_confirm = []
        for sale in sales:
            if cover(sale.payment_amount_authorized, sale.amount_to_pay):
                to_confirm.append(sale)
        if to_confirm:
            to_confirm = cls.browse(to_confirm)  # optimize cache
            cls.confirm(to_confirm)

    @property
    def credit_limit_amount(self):
        amount = super().credit_limit_amount
        return max(0, amount - self.payment_amount_authorized)
