# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.i18n import gettext
from trytond.model import (
    MatchMixin, ModelSQL, ModelView, fields, sequence_ordered)
from trytond.modules.company.model import CompanyValueMixin
from trytond.modules.party.exceptions import EraseError
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Eval
from trytond.transaction import Transaction

payment_direct_debit = fields.Boolean(
    "Direct Debit", help="Check if supplier does direct debit.")


class Party(metaclass=PoolMeta):
    __name__ = 'party.party'

    payment_direct_debit = fields.MultiValue(payment_direct_debit)
    payment_direct_debits = fields.One2Many(
        'party.party.payment_direct_debit', 'party', "Direct Debits")
    reception_direct_debits = fields.One2Many(
        'party.party.reception_direct_debit', 'party', "Direct Debits",
        help="Fill to debit automatically the customer.")
    payment_identical_parties = fields.Function(
        fields.Many2Many('party.party', None, None, "Identical Parties"),
        'get_payment_identical_parties')

    @classmethod
    def default_payment_direct_debit(cls, **pattern):
        return False

    def get_payment_identical_parties(self, name):
        return [p.id for p in self._payment_identical_parties()]

    def _payment_identical_parties(self):
        return set()

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


class PartyPaymentDirectDebit(ModelSQL, CompanyValueMixin):
    __name__ = 'party.party.payment_direct_debit'
    party = fields.Many2One(
        'party.party', "Party", ondelete='CASCADE',
        context={
            'company': Eval('company', -1),
            },
        depends={'company'})
    payment_direct_debit = payment_direct_debit


class PartyReceptionDirectDebit(
        sequence_ordered(), MatchMixin, ModelView, ModelSQL):
    __name__ = 'party.party.reception_direct_debit'

    party = fields.Many2One(
        'party.party', "Party", required=True, ondelete='CASCADE',
        context={
            'company': Eval('company', -1),
            },
        depends={'company'})
    journal = fields.Many2One(
        'account.payment.journal', "Journal", required=True)
    type = fields.Selection([
            ('line', "Line"),
            ('balance', "Balance"),
            ], "Type", required=True,
        help_selection={
            'line': "Make one payment per line.",
            'balance': "Make a single payment.",
            })
    company = fields.Function(
        fields.Many2One('company.company', "Company"),
        'on_change_with_company', searcher='search_company')
    currency = fields.Function(
        fields.Many2One('currency.currency', "Currency"),
        'on_change_with_currency')
    process_method = fields.Function(
        fields.Selection('get_process_methods', "Process Method"),
        'on_change_with_process_method')

    @classmethod
    def default_type(cls):
        return 'line'

    @fields.depends('journal')
    def on_change_with_company(self, name=None):
        return self.journal.company if self.journal else None

    @classmethod
    def search_company(cls, name, clause):
        nested = clause[0][len(name):]
        return [('journal.' + name + nested, *clause[1:])]

    @fields.depends('journal')
    def on_change_with_currency(self, name=None):
        return self.journal.currency if self.journal else None

    @classmethod
    def get_process_methods(cls):
        pool = Pool()
        Journal = pool.get('account.payment.journal')
        name = 'process_method'
        return Journal.fields_get([name])[name]['selection']

    @fields.depends('journal')
    def on_change_with_process_method(self, name=None):
        if self.journal:
            return self.journal.process_method

    @classmethod
    def get_pattern(cls, line, type='line'):
        return {
            'company': line.company.id,
            'currency': line.payment_currency.id,
            'type': type,
            }

    @classmethod
    def get_pattern_for_invoice(cls, invoice):
        return {
            'company': invoice.company.id,
            'currency': invoice.currency.id,
            }

    def get_payments(self, line=None, amount=None, date=None):
        pool = Pool()
        Date = pool.get('ir.date')
        assert not line or self.type == 'line'
        if date is None:
            with Transaction().set_context(company=self.company.id):
                date = Date.today()
        if line:
            date = min(line.maturity_date or date, date)
            if amount is None:
                amount = line.payment_amount
        if amount:
            for date, amount in self._compute(date, amount):
                yield self._get_payment(line, date, amount)

    def _compute(self, date, amount):
        yield date, amount

    def _get_payment(self, line, date, amount):
        pool = Pool()
        Payment = pool.get('account.payment')
        return Payment(
            company=self.company,
            journal=self.journal,
            kind='receivable',
            party=self.party,
            date=date,
            amount=amount,
            line=line)

    def get_balance_domain(self, date):
        return [
            ['OR',
                ('account.type.receivable', '=', True),
                ('account.type.payable', '=', True),
                ],
            ('party', '=', self.party.id),
            ('payment_currency', '=', self.currency.id),
            ('reconciliation', '=', None),
            ('payment_amount', '!=', 0),
            ('move_state', '=', 'posted'),
            ['OR',
                ('maturity_date', '<=', date),
                ('maturity_date', '=', None),
                ],
            ('payment_blocked', '!=', True),
            ('company', '=', self.company.id),
            ]

    def get_balance_pending_payment_domain(self):
        return [
            ('party', '=', self.party.id),
            ('currency', '=', self.currency.id),
            ('state', 'not in', ['succeeded', 'failed']),
            ('line', '=', None),
            ('company', '=', self.company.id),
            ]


class Replace(metaclass=PoolMeta):
    __name__ = 'party.replace'

    @classmethod
    def fields_to_replace(cls):
        return super().fields_to_replace() + [
            ('account.payment', 'party'),
            ('party.party.reception_direct_debit', 'party'),
            ]


class Erase(metaclass=PoolMeta):
    __name__ = 'party.erase'

    def check_erase_company(self, party, company):
        pool = Pool()
        Payment = pool.get('account.payment')
        super().check_erase_company(party, company)

        payments = Payment.search([
                ('party', '=', party.id),
                ('company', '=', company.id),
                ('state', 'not in', ['succeeded', 'failed']),
                ])
        if payments:
            raise EraseError(
                gettext('account_payment.msg_erase_party_pending_payment',
                    party=party.rec_name,
                    company=company.rec_name))
