# 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 io import BytesIO, TextIOWrapper

from coda import CODA

from trytond.i18n import gettext
from trytond.modules.account_statement.exceptions import ImportStatementError
from trytond.pool import Pool, PoolMeta


class StatementImportStart(metaclass=PoolMeta):
    __name__ = 'account.statement.import.start'

    @classmethod
    def __setup__(cls):
        super().__setup__()
        cls.file_format.selection.append(('coda', "CODA"))


class StatementImport(metaclass=PoolMeta):
    __name__ = 'account.statement.import'

    def parse_coda(self, encoding='windows-1252'):
        file_ = TextIOWrapper(BytesIO(self.start.file_), encoding=encoding)
        coda = CODA(file_)
        for coda_statement in coda.statements:
            statement = self.coda_statement(coda_statement)
            origins = []
            for move in coda_statement.moves:
                origins.extend(self.coda_origin(coda_statement, move))
            statement.origins = origins
            yield statement

    def coda_statement(self, coda_statement):
        pool = Pool()
        Statement = pool.get('account.statement')
        Journal = pool.get('account.statement.journal')

        statement = Statement()
        statement.name = coda_statement.new_sequence
        statement.company = self.start.company
        statement.journal = Journal.get_by_bank_account(
            statement.company, coda_statement.account,
            currency=coda_statement.account_currency)
        if not statement.journal:
            raise ImportStatementError(
                gettext('account_statement.msg_import_no_journal',
                    account=coda_statement.account))
        statement.date = coda_statement.creation_date
        statement.start_balance = coda_statement.old_balance
        statement.end_balance = coda_statement.new_balance
        statement.total_amount = (
            coda_statement.total_credit - coda_statement.total_debit)
        statement.number_of_lines = len(coda_statement.moves)
        return statement

    def coda_origin(self, coda_statement, move):
        pool = Pool()
        Origin = pool.get('account.statement.origin')

        origin = Origin()
        origin.number = move.sequence
        origin.date = move.entry_date
        origin.amount = move.amount
        origin.party = self.coda_party(coda_statement, move)
        # TODO select account using transaction codes
        origin.description = move.communication
        origin.information = self.coda_information(coda_statement, move)
        return [origin]

    def coda_party(self, coda_statement, move):
        pool = Pool()
        AccountNumber = pool.get('bank.account.number')

        if not move.counterparty_account:
            return
        numbers = AccountNumber.search(['OR',
                ('number', '=', move.counterparty_account),
                ('number_compact', '=', move.counterparty_account),
                ])
        if len(numbers) == 1:
            number, = numbers
            if number.account.owners:
                return number.account.owners[0]

    def coda_information(self, coda_statement, move):
        information = {}
        for name in [
                'bank_reference',
                'customer_reference',
                'counterparty_bic',
                'counterparty_account',
                'counterparty_name',
                'r_transaction',
                'category_purpose',
                'purpose',
                'transaction_family',
                'transaction_transaction',
                'transaction_category']:
            value = getattr(move, name)
            if value:
                information['coda_' + name] = value
        # TODO add move information
        return information
