# 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 datetime import date

from trytond.pool import PoolMeta, Pool
from trytond.model import fields
from trytond.wizard import StateTransition, Wizard
from trytond.transaction import Transaction


class Invoice(metaclass=PoolMeta):
    __name__ = 'account.invoice'

    def auto_reconcile(self):
        reconcile_lines = []
        Reconciliation = Pool().get('account.move.reconciliation')
        account_reconcile_id = self.account.id
        balance = []
        for ml in self.payment_lines:
            if not ml.reconciliation and ml.account.id == account_reconcile_id:
                reconcile_lines.append(ml)
                balance.append(ml.debit - ml.credit)

        for ml in self.move.lines:
            if ml.account.id == account_reconcile_id:
                reconcile_lines.append(ml)
                balance.append(ml.debit - ml.credit)

        if sum(balance) != 0:
            return

        if reconcile_lines:
            Reconciliation.create([{
                'lines': [('add', reconcile_lines)],
                'date': date.today(),
            }])

    @classmethod
    def create_commissions(cls, invoices):
        """
        This set Off set move in commission model from channel commission
        in booking
        """
        commissions = super(Invoice, cls).create_commissions(invoices)
        for commission in commissions:
            if commission.origin.__name__ == 'account.invoice.line':
                inv_line = commission.origin
                if inv_line.offset_move_line:
                    commission.offset_move_line = inv_line.offset_move_line.id
                    commission.save()

    @classmethod
    def _get_folio_origin(cls, line):
        if line.origin and line.origin.__name__ == 'hotel.folio':
            return line.origin

    @classmethod
    def process_offset_move(cls, invoice):
        """
        This method create the offset move in the booking, for to cross
        in the future with account move related to supplier invoice of
        agent in module commissions
        """
        Folio = Pool().get('hotel.folio')

        folios_target = []
        lines_ids = []
        bk = None
        for line in invoice.lines:
            folio = cls._get_folio_origin(line)
            if not folio:
                return
            folios_target.append((folio, line))
            bk = folio.booking

        if bk.channel and bk.collection_mode == 'anticipated':
            lines_ids = Folio.set_offset_commission_move(folios_target, bk)
        return lines_ids

    def _add_commission_payment(self):
        pool = Pool()
        Commission = pool.get('commission')
        MoveLine = pool.get('account.move.line')
        lines_ids = [line.id for line in self.lines]
        commissions = Commission.search([
            ('invoice_line', 'in', lines_ids)
        ])
        payment_lines = []
        to_reconcile = []
        for comm in commissions:
            if not comm.invoice_line or not comm.offset_move_line:
                return
            payment_lines.append(comm.offset_move_line.id)
            invoice = comm.invoice_line.invoice
            self.write([invoice], {'payment_lines': [
                ('add', payment_lines)
            ]})
            to_reconcile.append(comm.offset_move_line)
            for line in self.move.lines:
                if line.account == self.account and not line.reconciliation:
                    to_reconcile.append(line)
            if invoice.amount_to_pay == 0 and to_reconcile:
                MoveLine.reconcile(to_reconcile)

    @classmethod
    def _post(cls, invoices):
        for invoice in invoices:
            if invoice.type == 'in':
                super()._post([invoice])
                continue

            lines_ids = None
            if invoice.type == 'out' and invoice.agent:
                lines_ids = cls.process_offset_move(invoice)

            super()._post([invoice])
            if lines_ids:
                cls.write([invoice], {'payment_lines': [('add', lines_ids)]})
            if invoice.type == 'in':
                invoice._add_commission_payment()

            # Better add this for night audit
            # cls.set_booking_payments(invoice)
            # if not invoice.move.origin:
            #     return
            # invoice.auto_reconcile()

    @classmethod
    def set_booking_payments(cls, invoice):
        Config = Pool().get('hotel.configuration')
        config = Config.get_configuration()
        acc_advance = config.advance_account

        booking = None
        payments = []
        lines_paid = []
        bk_invoices = {}
        party = invoice.party
        for line in invoice.lines:
            if line.origin and line.origin.__name__ == 'hotel.folio':
                folio = line.origin
                booking = folio.booking
                payments.append(folio.payments)

        if booking:
            for inv in booking.invoices:
                if inv.state != 'posted':
                    continue
                if inv.party == invoice.party:
                    bk_invoices[inv.id] = inv
            payments.append(booking.payments)

        lines_paid = []
        lines_advance = []
        _bk_invoices = list(bk_invoices.values())
        for rec in set(payments):
            lines_paid.extend(cls.set_payments_lines(_bk_invoices, party, rec))
            lines_advance.extend(
                cls.set_payments_lines(_bk_invoices, party, rec, acc_advance))

        lines_invs_advance = {}
        if lines_paid or lines_advance:
            if acc_advance and lines_advance:
                lines_invs_advance = cls.do_move_writeoff_advance(
                        lines_advance, acc_advance, _bk_invoices)
        if lines_paid and len(_bk_invoices) == 1:
            lines_paid = [li.id for li in lines_paid]
            cls.write([invoice], {'payment_lines': [('add', lines_paid)]})

        if lines_invs_advance:
            for invoice in _bk_invoices:
                lines_paid = lines_invs_advance[invoice.id]
                cls.write([invoice], {
                    'payment_lines': [('add', lines_paid)],
                })
        return _bk_invoices

    @classmethod
    def set_payments_lines(cls, invoices, party, payments, account=None):
        lines_paid = []
        balance = 0
        account_id = account.id if account else invoices[0].account.id
        total_amount = sum(inv.total_amount for inv in invoices)
        for as_line in payments:
            if as_line.move and as_line.party == party:
                balance += as_line.amount
                if as_line.amount > total_amount or balance > total_amount:
                    break
                for ml in as_line.move.lines:
                    if ml.account.id == account_id and not ml.reconciliation:
                        lines_paid.append(ml)
        return lines_paid

    @classmethod
    def do_move_writeoff_advance(cls, lines, account, invoices):
        # Asiento de cancelacion de los anticipos en la reserva
        # conciliacion de los anticipos y abono de los pagos en factura
        pool = Pool()
        Move = pool.get('account.move')
        MoveLine = pool.get('account.move.line')
        Period = pool.get('account.period')
        Journal = pool.get('account.journal')
        Reconciliation = pool.get('account.move.reconciliation')
        journal, = Journal.search_read([
            ('type', '=', 'write-off')
            ], limit=1)

        inv = invoices[0]
        amount_paid = sum(li.credit for li in lines)
        sum_invoices = sum(invoice.total_amount for invoice in invoices)
        if sum_invoices != amount_paid:
            return {}
        period_id = Period.find(inv.company.id, date=inv.invoice_date)
        move, = Move.create([{
            'journal': journal['id'],
            'period': period_id,
            'date': inv.invoice_date,
            'state': 'draft',
            'description': inv.description,
        }])
        to_debit = []
        advance_lines = []
        for line in lines:
            advance_lines.append(line.id)
            to_debit.append({
                'description': inv.reference,
                'party': inv.party.id,
                'account': account.id,
                'debit': line.credit,
                'credit': 0,
                'move': move.id,
            })

        lines_invoices_paid = {}
        for invoice in invoices:
            line_id_pay, = MoveLine.create([{
                'description': inv.description,
                'party': inv.party.id,
                'account': inv.account.id,
                'debit': 0,
                'credit': invoice.total_amount,
                'move': move.id,
            }])
            lines_invoices_paid[invoice.id] = [line_id_pay]

        debit_lines = MoveLine.create(to_debit)
        Reconciliation.create([{
            'lines': [('add', debit_lines + advance_lines)],
            'date': date.today(),
        }])
        Move.post([move])
        return lines_invoices_paid

    @classmethod
    def set_advances_from_origin(cls, invoice):
        vouchers = []
        for line in invoice.lines:
            if line.origin and line.origin.__name__ == 'hotel.booking':
                booking = line.origin
                if booking.vouchers:
                    for voucher in booking.vouchers:
                        if invoice.party.id == voucher.party.id:
                            vouchers.append(voucher)
                            # FIXME must pass lines of move
        if vouchers:
            invoice.create_move_advance(set(vouchers))

    @classmethod
    def concile_booking_invoice(cls, record):
        if record.state != 'posted':
            return
        invoices = cls.set_booking_payments(record)
        for invoice in invoices:
            invoice.auto_reconcile()


class InvoiceLine(metaclass=PoolMeta):
    __name__ = 'account.invoice.line'
    offset_move_line = fields.Many2One('account.move.line', 'Offset Move Line')

    @classmethod
    def _get_origin(cls):
        return super(InvoiceLine, cls)._get_origin() + [
            'hotel.booking', 'hotel.folio'
        ]

    def get_move_lines(self):
        Config = Pool().get('hotel.configuration')
        config = Config.get_configuration()
        lines = super().get_move_lines()
        if config.accounting_revenue == 'recognise_revenue':
            for line in lines:
                line.account = config.recognise_account.id
        return lines


class InvoiceReconcileBooking(Wizard):
    'Invoice Reconcile Booking'
    __name__ = 'hotel.invoice_reconcile_booking'
    start_state = 'reconcile_'
    reconcile_ = StateTransition()

    def transition_reconcile_(self):
        Invoice = Pool().get('account.invoice')
        invoice_ids = Transaction().context['active_ids']

        if not invoice_ids:
            return 'end'

        invoices = Invoice.browse(invoice_ids)
        for inv in invoices:
            Invoice.concile_booking_invoice(inv)
        return 'end'
