# Fusion Accounting - Inter-Company Invoice Synchronization
# Copyright (C) 2026 Nexa Systems Inc. (https://nexasystems.ca)
# Original implementation for the Fusion Accounting module.
#
# When an invoice is posted in Company A to a partner that IS Company B,
# automatically creates a matching bill in Company B (and vice-versa).

import logging

from odoo import api, fields, models, Command, _
from odoo.exceptions import UserError, ValidationError

_logger = logging.getLogger(__name__)


class FusionInterCompanyRules(models.Model):
    """Extends res.company with inter-company invoice synchronization settings.

    When enabled, posting an invoice in one company that targets a partner
    linked to another company in the same database will automatically
    generate the corresponding counter-document (bill ↔ invoice) in the
    target company.
    """

    _inherit = 'res.company'

    # =====================================================================
    # Configuration Fields
    # =====================================================================

    fusion_intercompany_invoice_enabled = fields.Boolean(
        string="Inter-Company Invoice Sync",
        default=False,
        help="When enabled, posting an invoice/bill to a partner that "
             "represents another company will automatically create the "
             "corresponding counter-document in that company.",
    )
    fusion_intercompany_invoice_journal_id = fields.Many2one(
        comodel_name='account.journal',
        string="Inter-Company Journal",
        domain="[('type', 'in', ('sale', 'purchase'))]",
        help="Default journal used to create inter-company invoices/bills. "
             "If empty, the system will pick the first appropriate journal "
             "in the target company.",
    )

    # =====================================================================
    # Helpers
    # =====================================================================

    def _fusion_get_intercompany_target(self, partner):
        """Return the company record linked to the given partner, if any.

        A partner is considered an inter-company partner when it shares the
        same ``company_id`` reference or the partner *is* the commercial
        entity of another company in the system.

        :param partner: res.partner record
        :returns: res.company recordset (may be empty)
        """
        self.ensure_one()
        if not partner:
            return self.env['res.company']

        target_company = self.env['res.company'].sudo().search([
            ('partner_id', '=', partner.commercial_partner_id.id),
            ('id', '!=', self.id),
        ], limit=1)
        return target_company


class FusionInterCompanyAccountMove(models.Model):
    """Extends account.move to trigger inter-company invoice creation on post."""

    _inherit = 'account.move'

    fusion_intercompany_move_id = fields.Many2one(
        comodel_name='account.move',
        string="Inter-Company Counter-Document",
        copy=False,
        readonly=True,
        help="The matching invoice or bill that was auto-created in the "
             "partner's company.",
    )
    fusion_intercompany_source_id = fields.Many2one(
        comodel_name='account.move',
        string="Inter-Company Source Document",
        copy=False,
        readonly=True,
        help="The original invoice or bill in the originating company "
             "that triggered the creation of this document.",
    )

    # ------------------------------------------------------------------
    # Post Override
    # ------------------------------------------------------------------

    def _post(self, soft=True):
        """Override to trigger inter-company document creation after posting."""
        posted = super()._post(soft=soft)
        for move in posted:
            move._fusion_trigger_intercompany_sync()
        return posted

    def _fusion_trigger_intercompany_sync(self):
        """Check conditions and create the inter-company counter-document."""
        self.ensure_one()

        # Only applies to customer invoices / vendor bills
        if self.move_type not in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund'):
            return

        company = self.company_id
        if not company.fusion_intercompany_invoice_enabled:
            return

        # Already has a counter-document
        if self.fusion_intercompany_move_id:
            return

        partner = self.partner_id.commercial_partner_id
        target_company = company._fusion_get_intercompany_target(partner)
        if not target_company:
            return

        # Target company must also have the feature enabled
        if not target_company.fusion_intercompany_invoice_enabled:
            return

        try:
            self._create_intercompany_invoice(target_company)
        except Exception as exc:
            _logger.warning(
                "Fusion Inter-Company: failed to create counter-document "
                "for %s (id=%s): %s",
                self.name, self.id, exc,
            )

    # ------------------------------------------------------------------
    # Counter-Document Creation
    # ------------------------------------------------------------------

    _MOVE_TYPE_MAP = {
        'out_invoice': 'in_invoice',
        'out_refund': 'in_refund',
        'in_invoice': 'out_invoice',
        'in_refund': 'out_refund',
    }

    def _create_intercompany_invoice(self, target_company):
        """Create the counter-document in *target_company*.

        Maps:
        - Customer Invoice  →  Vendor Bill   (and vice-versa)
        - Customer Credit Note  →  Vendor Credit Note

        Line items are copied with accounts resolved in the target company's
        chart of accounts.  Taxes are **not** copied to avoid cross-company
        tax configuration issues; the target company's fiscal position and
        default taxes will apply instead.

        :param target_company: res.company record of the receiving company
        """
        self.ensure_one()
        target_move_type = self._MOVE_TYPE_MAP.get(self.move_type)
        if not target_move_type:
            return

        # Determine journal in target company
        journal = target_company.fusion_intercompany_invoice_journal_id
        if not journal or journal.company_id != target_company:
            journal_type = 'purchase' if target_move_type.startswith('in_') else 'sale'
            journal = self.env['account.journal'].sudo().search([
                ('company_id', '=', target_company.id),
                ('type', '=', journal_type),
            ], limit=1)

        if not journal:
            _logger.warning(
                "Fusion Inter-Company: no %s journal found in company %s",
                'purchase' if target_move_type.startswith('in_') else 'sale',
                target_company.name,
            )
            return

        # Build the partner reference: the originating company's partner
        source_partner = self.company_id.partner_id

        # Prepare invoice line values
        line_vals = []
        for line in self.invoice_line_ids.filtered(lambda l: l.display_type == 'product'):
            line_vals.append(Command.create({
                'name': line.name or '/',
                'quantity': line.quantity,
                'price_unit': line.price_unit,
                'discount': line.discount,
                'product_id': line.product_id.id if line.product_id else False,
                'product_uom_id': line.product_uom_id.id if line.product_uom_id else False,
                'analytic_distribution': line.analytic_distribution,
            }))

        if not line_vals:
            _logger.info(
                "Fusion Inter-Company: no product lines to copy for %s (id=%s)",
                self.name, self.id,
            )
            return

        # Create the counter-document in sudo context of target company
        move_vals = {
            'move_type': target_move_type,
            'journal_id': journal.id,
            'company_id': target_company.id,
            'partner_id': source_partner.id,
            'invoice_date': self.invoice_date,
            'date': self.date,
            'ref': _("IC: %s", self.name),
            'narration': self.narration,
            'currency_id': self.currency_id.id,
            'invoice_line_ids': line_vals,
            'fusion_intercompany_source_id': self.id,
        }

        new_move = self.env['account.move'].sudo().with_company(
            target_company
        ).create(move_vals)

        # Link the two documents
        self.sudo().write({
            'fusion_intercompany_move_id': new_move.id,
        })

        _logger.info(
            "Fusion Inter-Company: created %s (id=%s) in %s from %s (id=%s)",
            new_move.name, new_move.id, target_company.name,
            self.name, self.id,
        )
        return new_move
