"""
Tests for Parser_1C (account_bank_statement_1c_import).

Validates: Requirements 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.9
"""
import base64

from odoo.exceptions import UserError
from odoo.tests.common import TransactionCase


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

COMPANY_INN = "7700000001"
COMPANY_KPP = "770001001"

PARTNER_INN = "7700000002"
PARTNER_KPP = "770001002"

VALID_TXT_TEMPLATE = """\
1CClientBankExchange
ВерсияФормата=1.03
Кодировка=Windows
Отправитель=Бухгалтерия предприятия
ДатаСоздания=01.01.2024
ВремяСоздания=12:00:00
ДатаНачала=01.01.2024
ДатаКонца=31.01.2024
РасчСчет=40702810000000000001
СекцияРасчСчет
ДатаНачала=01.01.2024
ДатаКонца=31.01.2024
РасчСчет=40702810000000000001
НачальныйОстаток=10000.00
ВсегоПоступило=5000.00
ВсегоСписано=3000.00
КонечныйОстаток=12000.00
КонецРасчСчет
{transactions}КонецФайла
"""

TRANSACTION_PAYER = """\
СекцияДокумент=Платежное поручение
Номер=1
Дата=15.01.2024
Сумма=1000.00
ПлательщикСчет=40702810000000000001
ДатаСписано=15.01.2024
ПлательщикИНН={company_inn}
ПлательщикКПП={company_kpp}
Плательщик=ООО Наша Компания
Плательщик1=ООО Наша Компания
ПлательщикРасчСчет=40702810000000000001
ПлательщикБанк1=Банк Плательщика
ПлательщикБИК=044525001
ПлательщикКорсчет=30101810400000000001
ПолучательСчет=40702810000000000002
ПолучательИНН={partner_inn}
ПолучательКПП={partner_kpp}
Получатель=ООО Контрагент
Получатель1=ООО Контрагент
ПолучательРасчСчет=40702810000000000002
ПолучательБанк1=Банк Получателя
ПолучательБИК=044525002
ПолучательКорсчет=30101810400000000002
НазначениеПлатежа=Оплата по договору №1
КонецДокумента
"""

TRANSACTION_RECIPIENT = """\
СекцияДокумент=Платежное поручение
Номер=2
Дата=16.01.2024
Сумма=2000.00
ПлательщикСчет=40702810000000000002
ДатаПоступило=16.01.2024
ПлательщикИНН={partner_inn}
ПлательщикКПП={partner_kpp}
Плательщик=ООО Контрагент
Плательщик1=ООО Контрагент
ПлательщикРасчСчет=40702810000000000002
ПлательщикБанк1=Банк Плательщика
ПлательщикБИК=044525002
ПлательщикКорсчет=30101810400000000002
ПолучательСчет=40702810000000000001
ПолучательИНН={company_inn}
ПолучательКПП={company_kpp}
Получатель=ООО Наша Компания
Получатель1=ООО Наша Компания
ПолучательРасчСчет=40702810000000000001
ПолучательБанк1=Банк Получателя
ПолучательБИК=044525001
ПолучательКорсчет=30101810400000000001
НазначениеПлатежа=Поступление по договору №2
КонецДокумента
"""

TRANSACTION_UNKNOWN_INN = """\
СекцияДокумент=Платежное поручение
Номер=3
Дата=17.01.2024
Сумма=500.00
ПлательщикСчет=40702810000000000003
ДатаСписано=17.01.2024
ПлательщикИНН=9999999999
ПлательщикКПП=999999999
Плательщик=ООО Чужая Компания
Плательщик1=ООО Чужая Компания
ПлательщикРасчСчет=40702810000000000003
ПлательщикБанк1=Чужой Банк
ПлательщикБИК=044525003
ПлательщикКорсчет=30101810400000000003
ПолучательСчет=40702810000000000004
ПолучательИНН=8888888888
ПолучательКПП=888888888
Получатель=ООО Другая Компания
Получатель1=ООО Другая Компания
ПолучательРасчСчет=40702810000000000004
ПолучательБанк1=Другой Банк
ПолучательБИК=044525004
ПолучательКорсчет=30101810400000000004
НазначениеПлатежа=Транзакция без нашей компании
КонецДокумента
"""


def _encode(text):
    """Encode text to base64 as cp1251 (as the wizard expects)."""
    return base64.b64encode(text.encode('cp1251'))


def _make_txt(transactions=""):
    return VALID_TXT_TEMPLATE.format(transactions=transactions)


def _payer_tx():
    return TRANSACTION_PAYER.format(
        company_inn=COMPANY_INN, company_kpp=COMPANY_KPP,
        partner_inn=PARTNER_INN, partner_kpp=PARTNER_KPP,
    )


def _recipient_tx():
    return TRANSACTION_RECIPIENT.format(
        company_inn=COMPANY_INN, company_kpp=COMPANY_KPP,
        partner_inn=PARTNER_INN, partner_kpp=PARTNER_KPP,
    )


def _unknown_inn_tx():
    return TRANSACTION_UNKNOWN_INN


# ---------------------------------------------------------------------------
# Fixtures / setUp helpers
# ---------------------------------------------------------------------------

class _Base(TransactionCase):
    """Common setUp: company VAT, bank journal with use_in_bank_statement."""

    def setUp(self):
        super().setUp()
        # Set company VAT so the parser can match INN
        self.env.company.write({'vat': COMPANY_INN})

        # Create (or find) a bank journal with use_in_bank_statement=True
        self.journal = self.env['account.journal'].search(
            [('type', '=', 'bank'), ('use_in_bank_statement', '=', True)],
            limit=1,
        )
        if not self.journal:
            self.journal = self.env['account.journal'].create({
                'name': 'Test Bank Journal',
                'type': 'bank',
                'code': 'TBNK',
                'use_in_bank_statement': True,
            })

    def _wizard(self, file_content_bytes, file_name='statement.txt'):
        """Create an InvoiceImportWizard record."""
        return self.env['invoice.import.wizard'].create({
            'file': base64.b64encode(file_content_bytes),
            'file_name': file_name,
        })


# ---------------------------------------------------------------------------
# TestParser1CBasic
# ---------------------------------------------------------------------------

class TestParser1CBasic(_Base):
    """Validates: Requirements 2.1, 2.2, 2.3"""

    def test_valid_txt_parses_without_exception(self):
        """Req 2.1 — correct TXT file is parsed without exceptions."""
        content = _make_txt().encode('cp1251')
        wizard = self._wizard(content, 'statement.txt')
        # action_import_invoice should not raise
        wizard.action_import_invoice()

    def test_wrong_extension_raises_user_error(self):
        """Req 2.2 — non-.txt extension raises UserError."""
        content = _make_txt().encode('cp1251')
        wizard = self._wizard(content, 'statement.csv')
        with self.assertRaises(UserError):
            wizard.action_import_invoice()

    def test_no_file_raises_user_error(self):
        """Req 2.3 — missing file raises UserError."""
        wizard = self.env['invoice.import.wizard'].create({
            'file': False,
            'file_name': 'statement.txt',
        })
        with self.assertRaises(UserError):
            wizard.action_import_invoice()


# ---------------------------------------------------------------------------
# TestParser1CLines
# ---------------------------------------------------------------------------

class TestParser1CLines(_Base):
    """Validates: Requirements 2.4, 2.5, 2.6"""

    def _import_and_get_lines(self, transactions_text):
        content = _make_txt(transactions_text).encode('cp1251')
        wizard = self._wizard(content, 'statement.txt')
        wizard.action_import_invoice()
        return self.env['account.bank.statement.line'].search([], order='id desc')

    def test_company_as_payer_creates_negative_amount(self):
        """Req 2.4 — company INN as payer → negative amount line."""
        lines = self._import_and_get_lines(_payer_tx())
        payer_lines = lines.filtered(lambda l: l.payment_ref == '1')
        self.assertTrue(payer_lines, "Expected a statement line with payment_ref='1'")
        self.assertLess(payer_lines[0].amount, 0, "Amount should be negative when company is payer")

    def test_company_as_recipient_creates_positive_amount(self):
        """Req 2.5 — company INN as recipient → positive amount line."""
        lines = self._import_and_get_lines(_recipient_tx())
        recipient_lines = lines.filtered(lambda l: l.payment_ref == '2')
        self.assertTrue(recipient_lines, "Expected a statement line with payment_ref='2'")
        self.assertGreater(recipient_lines[0].amount, 0, "Amount should be positive when company is recipient")

    def test_duplicate_line_is_skipped(self):
        """Req 2.6 — importing the same transaction twice does not create a duplicate."""
        tx = _payer_tx()
        content = _make_txt(tx).encode('cp1251')

        # First import
        self._wizard(content, 'statement.txt').action_import_invoice()
        count_after_first = self.env['account.bank.statement.line'].search_count(
            [('payment_ref', '=', '1')]
        )

        # Second import of the same file
        self._wizard(content, 'statement.txt').action_import_invoice()
        count_after_second = self.env['account.bank.statement.line'].search_count(
            [('payment_ref', '=', '1')]
        )

        self.assertEqual(count_after_first, count_after_second,
                         "Duplicate transaction should be skipped on second import")


# ---------------------------------------------------------------------------
# TestParser1CPartner
# ---------------------------------------------------------------------------

class TestParser1CPartner(_Base):
    """Validates: Requirements 2.7, 2.9"""

    def test_unknown_inn_kpp_creates_new_partner(self):
        """Req 2.7 — unknown INN/KPP → new partner of type 'company' is created."""
        # Ensure partner does not exist
        self.env['res.partner'].search([('vat', '=', PARTNER_INN)]).unlink()

        content = _make_txt(_payer_tx()).encode('cp1251')
        self._wizard(content, 'statement.txt').action_import_invoice()

        partner = self.env['res.partner'].search([('vat', '=', PARTNER_INN)], limit=1)
        self.assertTrue(partner, "A new partner should have been created")
        self.assertEqual(partner.company_type, 'company',
                         "New partner should have company_type='company'")

    def test_unknown_company_inn_in_transaction_raises_user_error(self):
        """Req 2.9 — company INN not found as payer or recipient → UserError."""
        content = _make_txt(_unknown_inn_tx()).encode('cp1251')
        wizard = self._wizard(content, 'statement.txt')
        with self.assertRaises(UserError):
            wizard.action_import_invoice()
