"""
Tests for Template_Engine (account_move_templates).

Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5
"""
from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase


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

def _get_account(env, account_type='asset_current'):
    """Return an existing account of the given type, or create one."""
    account = env['account.account'].search(
        [('account_type', '=', account_type)], limit=1
    )
    if not account:
        account = env['account.account'].create({
            'name': f'Test Account ({account_type})',
            'code': f'TST{account_type[:4].upper()}',
            'account_type': account_type,
        })
    return account


def _make_template(env, name='Test Template', lines=None):
    """Create an AccountMoveTemplate with the given lines.

    lines: list of dicts with keys: account_id, move_type, percent, line_type
    """
    vals = {'name': name}
    if lines is not None:
        vals['line_ids'] = [(0, 0, line) for line in lines]
    return env['account.move.template'].create(vals)


# ---------------------------------------------------------------------------
# TestMoveTemplateValidation
# ---------------------------------------------------------------------------

class TestMoveTemplateValidation(TransactionCase):
    """Validates: Requirements 3.1, 3.2, 3.3, 3.4"""

    def setUp(self):
        super().setUp()
        self.account_debit = _get_account(self.env, 'asset_current')
        self.account_credit = _get_account(self.env, 'liability_current')

    def _line(self, move_type, percent, account=None):
        if account is None:
            account = self.account_debit if move_type == 'debit' else self.account_credit
        return {
            'account_id': account.id,
            'move_type': move_type,
            'percent': percent,
            'line_type': 'product',
        }

    def test_no_lines_raises_validation_error(self):
        """Req 3.1 — template without lines raises ValidationError."""
        with self.assertRaises(ValidationError):
            _make_template(self.env, name='Empty Template', lines=[])

    def test_imbalanced_percentages_raises_validation_error(self):
        """Req 3.2 — debit sum != credit sum raises ValidationError."""
        with self.assertRaises(ValidationError):
            _make_template(self.env, name='Imbalanced', lines=[
                self._line('debit', 60.0),
                self._line('credit', 40.0),
            ])

    def test_balanced_percentages_saves_without_error(self):
        """Req 3.3 — debit sum == credit sum saves without error."""
        template = _make_template(self.env, name='Balanced', lines=[
            self._line('debit', 100.0),
            self._line('credit', 100.0),
        ])
        self.assertTrue(template.id, "Template should be saved with a valid ID")

    def test_percent_below_minimum_raises_validation_error(self):
        """Req 3.4 — percent < 0.01 raises ValidationError."""
        with self.assertRaises(ValidationError):
            _make_template(self.env, name='Low Percent', lines=[
                self._line('debit', 0.001),
                self._line('credit', 0.001),
            ])

    def test_percent_above_maximum_raises_validation_error(self):
        """Req 3.4 — percent > 100.0 raises ValidationError."""
        with self.assertRaises(ValidationError):
            _make_template(self.env, name='High Percent', lines=[
                self._line('debit', 101.0),
                self._line('credit', 101.0),
            ])


# ---------------------------------------------------------------------------
# TestMoveTemplateWizard
# ---------------------------------------------------------------------------

class TestMoveTemplateWizard(TransactionCase):
    """Validates: Requirement 3.5"""

    def setUp(self):
        super().setUp()
        self.account_debit = _get_account(self.env, 'asset_current')
        self.account_credit = _get_account(self.env, 'liability_current')

        # Create a balanced template: 100% debit / 100% credit
        self.template = self.env['account.move.template'].create({
            'name': 'Wizard Test Template',
            'line_ids': [
                (0, 0, {
                    'account_id': self.account_debit.id,
                    'move_type': 'debit',
                    'percent': 100.0,
                    'line_type': 'product',
                }),
                (0, 0, {
                    'account_id': self.account_credit.id,
                    'move_type': 'credit',
                    'percent': 100.0,
                    'line_type': 'product',
                }),
            ],
        })

        # Create a source document that uses the mixin (account.move itself
        # does not use the mixin, so we create a minimal move to act as source)
        self.source_move = self.env['account.move'].create({
            'move_type': 'entry',
            'state': 'draft',
            'line_ids': [
                (0, 0, {
                    'account_id': self.account_debit.id,
                    'debit': 0.0,
                    'credit': 0.0,
                    'name': 'Source line',
                }),
            ],
        })

    def test_apply_template_creates_draft_move_with_correct_distribution(self):
        """Req 3.5 — applying template via wizard creates a draft account.move
        with correct account distribution."""
        base_amount = 1000.0

        wizard = self.env['account.move.template.wizard'].create({
            'res_model': 'account.move',
            'res_id': self.source_move.id,
            'template_id': self.template.id,
            'amount': base_amount,
        })

        # Trigger onchange to populate draft lines
        wizard._onchange_compute_draft_lines()

        # Verify draft lines were computed
        self.assertEqual(len(wizard.draft_line_ids), 2,
                         "Wizard should have 2 draft lines (one debit, one credit)")

        debit_line = wizard.draft_line_ids.filtered(lambda l: l.move_type == 'debit')
        credit_line = wizard.draft_line_ids.filtered(lambda l: l.move_type == 'credit')

        self.assertAlmostEqual(debit_line.amount, base_amount,
                               msg="Debit line amount should equal base_amount * 100%")
        self.assertAlmostEqual(credit_line.amount, base_amount,
                               msg="Credit line amount should equal base_amount * 100%")

        # Post the wizard — creates the account.move
        wizard.action_post()

        # Reload source move and check linked moves
        self.source_move.invalidate_recordset()
        linked_moves = self.source_move.move_ids

        self.assertTrue(linked_moves, "A new account.move should be linked to the source")

        new_move = linked_moves[-1]
        self.assertEqual(new_move.state, 'draft',
                         "Created move should be in draft state")

        move_lines = new_move.line_ids
        debit_move_line = move_lines.filtered(lambda l: l.debit > 0)
        credit_move_line = move_lines.filtered(lambda l: l.credit > 0)

        self.assertTrue(debit_move_line, "Move should have a debit line")
        self.assertTrue(credit_move_line, "Move should have a credit line")

        self.assertAlmostEqual(debit_move_line[0].debit, base_amount,
                               msg="Debit amount on move line should match base_amount")
        self.assertAlmostEqual(credit_move_line[0].credit, base_amount,
                               msg="Credit amount on move line should match base_amount")

        self.assertEqual(debit_move_line[0].account_id, self.account_debit,
                         "Debit line should use the debit account from template")
        self.assertEqual(credit_move_line[0].account_id, self.account_credit,
                         "Credit line should use the credit account from template")
