# Part of Fusion Accounting. See LICENSE file for full copyright and licensing details.

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


class FusionPartnerFollowup(models.Model):
    """Extends the partner model with payment follow-up capabilities.

    Adds fields and methods for tracking overdue invoices, determining
    the appropriate follow-up level, and launching the follow-up
    workflow directly from the partner form.
    """

    _inherit = 'res.partner'

    # ---- Follow-up Fields ----
    followup_level_id = fields.Many2one(
        comodel_name='fusion.followup.level',
        string="Follow-up Level",
        company_dependent=True,
        tracking=True,
        help="Current follow-up escalation level for this partner.",
    )
    followup_next_action_date = fields.Date(
        string="Next Follow-up Date",
        company_dependent=True,
        tracking=True,
        help="Date on which the next follow-up action should be performed.",
    )
    followup_responsible_id = fields.Many2one(
        comodel_name='res.users',
        string="Follow-up Responsible",
        company_dependent=True,
        tracking=True,
        help="User responsible for managing payment collection for this partner.",
    )

    # ---- Computed Indicators ----
    fusion_overdue_amount = fields.Monetary(
        string="Total Overdue Amount",
        compute='_compute_fusion_overdue_amount',
        currency_field='currency_id',
        help="Total amount of overdue receivables for the current company.",
    )
    fusion_overdue_count = fields.Integer(
        string="Overdue Invoice Count",
        compute='_compute_fusion_overdue_amount',
        help="Number of overdue invoices for the current company.",
    )

    # --------------------------------------------------
    # Computed Fields
    # --------------------------------------------------

    @api.depends('credit')
    def _compute_fusion_overdue_amount(self):
        """Compute the total overdue receivable amount and invoice count.

        Uses the receivable move lines that are posted, unreconciled,
        and past their maturity date.
        """
        today = fields.Date.context_today(self)
        for partner in self:
            overdue_data = partner.get_overdue_invoices()
            partner.fusion_overdue_amount = sum(
                line.amount_residual for line in overdue_data
            )
            partner.fusion_overdue_count = len(overdue_data.mapped('move_id'))

    # --------------------------------------------------
    # Public Methods
    # --------------------------------------------------

    def get_overdue_invoices(self):
        """Return unpaid receivable move lines that are past due.

        Searches for posted, unreconciled journal items on receivable
        accounts where the maturity date is earlier than today and
        there is still an outstanding balance.

        :returns: An ``account.move.line`` recordset.
        """
        self.ensure_one()
        today = fields.Date.context_today(self)
        return self.env['account.move.line'].search([
            ('partner_id', '=', self.commercial_partner_id.id),
            ('company_id', '=', self.env.company.id),
            ('account_id.account_type', '=', 'asset_receivable'),
            ('parent_state', '=', 'posted'),
            ('reconciled', '=', False),
            ('amount_residual', '>', 0),
            ('date_maturity', '<', today),
        ])

    def get_overdue_amount(self):
        """Return the total overdue receivable amount for this partner.

        :returns: A float representing the overdue monetary amount.
        """
        self.ensure_one()
        overdue_lines = self.get_overdue_invoices()
        return sum(overdue_lines.mapped('amount_residual'))

    def action_open_followup(self):
        """Open the follow-up form view for this partner.

        Locates or creates the ``fusion.followup.line`` record for the
        current partner and company, then opens the form view for it.

        :returns: An ``ir.actions.act_window`` dictionary.
        """
        self.ensure_one()
        followup_line = self.env['fusion.followup.line'].search([
            ('partner_id', '=', self.id),
            ('company_id', '=', self.env.company.id),
        ], limit=1)

        if not followup_line:
            # Assign first level automatically
            first_level = self.env['fusion.followup.level'].search([
                ('company_id', '=', self.env.company.id),
            ], order='sequence, id', limit=1)
            followup_line = self.env['fusion.followup.line'].create({
                'partner_id': self.id,
                'company_id': self.env.company.id,
                'followup_level_id': first_level.id if first_level else False,
            })

        return {
            'type': 'ir.actions.act_window',
            'name': _("Payment Follow-up: %s", self.display_name),
            'res_model': 'fusion.followup.line',
            'res_id': followup_line.id,
            'view_mode': 'form',
            'target': 'current',
        }

    # --------------------------------------------------
    # Scheduled Actions
    # --------------------------------------------------

    @api.model
    def compute_partners_needing_followup(self):
        """Scheduled action: find partners with overdue invoices and create
        or update their follow-up tracking records.

        This method is called daily by ``ir.cron``. It scans for partners
        that have at least one overdue receivable and ensures each one has
        a ``fusion.followup.line`` record. Existing records are refreshed
        so their computed fields stay current.

        :returns: ``True``
        """
        today = fields.Date.context_today(self)
        companies = self.env['res.company'].search([])

        for company in companies:
            # Find partners with overdue receivables in this company
            overdue_lines = self.env['account.move.line'].search([
                ('company_id', '=', company.id),
                ('account_id.account_type', '=', 'asset_receivable'),
                ('parent_state', '=', 'posted'),
                ('reconciled', '=', False),
                ('date_maturity', '<', today),
                ('amount_residual', '>', 0),
            ])

            partner_ids = overdue_lines.mapped(
                'partner_id.commercial_partner_id'
            ).ids

            if not partner_ids:
                continue

            # Fetch existing tracking records for these partners
            existing_lines = self.env['fusion.followup.line'].search([
                ('partner_id', 'in', partner_ids),
                ('company_id', '=', company.id),
            ])
            existing_partner_ids = existing_lines.mapped('partner_id').ids

            # Determine the first follow-up level for new records
            first_level = self.env['fusion.followup.level'].search([
                ('company_id', '=', company.id),
            ], order='sequence, id', limit=1)

            # Create tracking records for partners that don't have one yet
            new_partner_ids = set(partner_ids) - set(existing_partner_ids)
            if new_partner_ids:
                self.env['fusion.followup.line'].create([{
                    'partner_id': pid,
                    'company_id': company.id,
                    'followup_level_id': first_level.id if first_level else False,
                } for pid in new_partner_ids])

            # Refresh computed fields on all relevant records
            all_lines = self.env['fusion.followup.line'].search([
                ('partner_id', 'in', partner_ids),
                ('company_id', '=', company.id),
            ])
            all_lines.compute_followup_status()

        return True
