# Fusion Accounting - Move Line Extensions
# Bank-line exclusion flag, tax-closing safeguards, and report shadowing

from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.tools import SQL


class FusionAccountMoveLine(models.Model):
    """Extends journal items with a computed bank-line exclusion flag,
    guards against tax manipulation on closing entries, and provides
    utilities for building temporary shadow tables used by analytic
    and budget reports."""

    _name = "account.move.line"
    _inherit = "account.move.line"

    # ---- Fields ----
    exclude_bank_lines = fields.Boolean(
        compute='_compute_exclude_bank_lines',
        store=True,
    )

    # ---- Computed ----
    @api.depends('journal_id')
    def _compute_exclude_bank_lines(self):
        """Flag lines whose account differs from their journal's
        default account, used to filter non-bank entries in bank
        journal views."""
        for ml in self:
            ml.exclude_bank_lines = (
                ml.account_id != ml.journal_id.default_account_id
            )

    # ---- Constraints ----
    @api.constrains('tax_ids', 'tax_tag_ids')
    def _check_taxes_on_closing_entries(self):
        """Prevent taxes from being added to tax-closing move lines."""
        for ml in self:
            if ml.move_id.tax_closing_report_id and (ml.tax_ids or ml.tax_tag_ids):
                raise UserError(
                    _("Tax lines are not permitted on tax-closing entries.")
                )

    # ---- Tax Computation Override ----
    @api.depends('product_id', 'product_uom_id', 'move_id.tax_closing_report_id')
    def _compute_tax_ids(self):
        """Skip automatic tax computation for lines on tax-closing
        moves, which might otherwise trigger the constraint above."""
        non_closing_lines = self.filtered(
            lambda ln: not ln.move_id.tax_closing_report_id
        )
        (self - non_closing_lines).tax_ids = False
        super(FusionAccountMoveLine, non_closing_lines)._compute_tax_ids()

    # ---- Report Shadow Table Utility ----
    @api.model
    def _prepare_aml_shadowing_for_report(self, change_equivalence_dict):
        """Build SQL fragments for creating a temporary table that
        mirrors ``account_move_line`` but substitutes selected columns
        with alternative expressions (e.g. analytic or budget data).

        :param change_equivalence_dict:
            Mapping ``{field_name: sql_expression}`` where each value
            replaces the corresponding column in the shadow table.
        :returns:
            A tuple ``(insert_columns, select_expressions)`` of SQL
            objects suitable for ``INSERT INTO ... SELECT ...``.
        """
        field_metadata = self.env['account.move.line'].fields_get()
        self.env.cr.execute(
            "SELECT column_name FROM information_schema.columns "
            "WHERE table_name='account_move_line'"
        )
        db_columns = {
            row[0] for row in self.env.cr.fetchall() if row[0] in field_metadata
        }

        select_parts = []
        for col_name in db_columns:
            if col_name in change_equivalence_dict:
                select_parts.append(SQL(
                    "%(src)s AS %(alias)s",
                    src=change_equivalence_dict[col_name],
                    alias=SQL('"account_move_line.%s"', SQL(col_name)),
                ))
            else:
                col_meta = field_metadata[col_name]
                if col_meta.get("translate"):
                    pg_type = SQL('jsonb')
                else:
                    pg_type = SQL(
                        self.env['account.move.line']._fields[col_name].column_type[0]
                    )
                select_parts.append(SQL(
                    "CAST(NULL AS %(pg_type)s) AS %(alias)s",
                    pg_type=pg_type,
                    alias=SQL('"account_move_line.%s"', SQL(col_name)),
                ))

        insert_cols = SQL(', ').join(SQL.identifier(c) for c in db_columns)
        select_clause = SQL(', ').join(select_parts)
        return insert_cols, select_clause
