# Fusion Accounting - Report Send Wizard
# Handles email dispatch and download of accounting reports to partners

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


class AccountReportSend(models.TransientModel):
    """Wizard providing a unified interface for sending accounting reports
    via email and/or downloading them as PDF attachments."""

    _name = 'account.report.send'
    _description = "Fusion Accounting Report Dispatch Wizard"

    partner_ids = fields.Many2many(
        comodel_name='res.partner',
        compute='_compute_partner_ids',
    )
    mode = fields.Selection(
        selection=[
            ('single', "Single Recipient"),
            ('multi', "Multiple Recipients"),
        ],
        compute='_compute_mode',
        readonly=False,
        store=True,
    )

    # -- Download options --
    enable_download = fields.Boolean()
    checkbox_download = fields.Boolean(string="Download")

    # -- Email options --
    enable_send_mail = fields.Boolean(default=True)
    checkbox_send_mail = fields.Boolean(string="Email", default=True)

    display_mail_composer = fields.Boolean(compute='_compute_mail_ui_state')
    warnings = fields.Json(compute='_compute_warnings')
    send_mail_readonly = fields.Boolean(compute='_compute_mail_ui_state')

    mail_template_id = fields.Many2one(
        comodel_name='mail.template',
        string="Email template",
        domain="[('model', '=', 'res.partner')]",
    )

    account_report_id = fields.Many2one(
        comodel_name='account.report',
        string="Report",
    )
    report_options = fields.Json()

    mail_lang = fields.Char(
        string="Lang",
        compute='_compute_mail_lang',
    )
    mail_partner_ids = fields.Many2many(
        comodel_name='res.partner',
        string="Recipients",
        compute='_compute_mail_partner_ids',
        store=True,
        readonly=False,
    )
    mail_subject = fields.Char(
        string="Subject",
        compute='_compute_mail_subject_body',
        store=True,
        readonly=False,
    )
    mail_body = fields.Html(
        string="Contents",
        sanitize_style=True,
        compute='_compute_mail_subject_body',
        store=True,
        readonly=False,
    )
    mail_attachments_widget = fields.Json(
        compute='_compute_mail_attachments_widget',
        store=True,
        readonly=False,
    )

    # -------------------------------------------------------------------------
    # Default values
    # -------------------------------------------------------------------------

    @api.model
    def default_get(self, fields_list):
        # EXTENDS 'base'
        defaults = super().default_get(fields_list)

        ctx_options = self.env.context.get('default_report_options', {})
        if 'account_report_id' in fields_list and 'account_report_id' not in defaults:
            defaults['account_report_id'] = ctx_options.get('report_id', False)
            defaults['report_options'] = ctx_options

        return defaults

    # -------------------------------------------------------------------------
    # Helper methods
    # -------------------------------------------------------------------------

    @api.model
    def _get_mail_field_value(self, partner, mail_template, mail_lang, field, **kwargs):
        """Render a single field from the mail template for a given partner."""
        if not mail_template:
            return
        rendered = mail_template.with_context(lang=mail_lang)._render_field(
            field, partner.ids, **kwargs
        )
        return rendered[partner._origin.id]

    def _get_default_mail_attachments_widget(self, partner, mail_template):
        """Combine report placeholder attachments with template-defined attachments."""
        placeholder_data = self._get_placeholder_mail_attachments_data(partner)
        template_data = self._get_mail_template_attachments_data(mail_template)
        return placeholder_data + template_data

    def _get_wizard_values(self):
        """Serialize the current wizard state into a storable dictionary."""
        self.ensure_one()
        opts = self.report_options
        if not opts.get('partner_ids', []):
            opts['partner_ids'] = self.partner_ids.ids
        return {
            'mail_template_id': self.mail_template_id.id,
            'checkbox_download': self.checkbox_download,
            'checkbox_send_mail': self.checkbox_send_mail,
            'report_options': opts,
        }

    def _get_placeholder_mail_attachments_data(self, partner):
        """Generate placeholder attachment metadata for the report PDF.

        Returns a list with one dict per placeholder containing:
            - id: unique placeholder identifier string
            - name: display filename
            - mimetype: MIME type of the file
            - placeholder: True (prevents download/deletion in the widget)
        """
        self.ensure_one()
        pdf_name = (
            f"{partner.name} - "
            f"{self.account_report_id.get_default_report_filename(self.report_options, 'pdf')}"
        )
        return [{
            'id': f'placeholder_{pdf_name}',
            'name': pdf_name,
            'mimetype': 'application/pdf',
            'placeholder': True,
        }]

    @api.model
    def _get_mail_template_attachments_data(self, mail_template):
        """Build attachment metadata from files linked to the mail template."""
        return [
            {
                'id': att.id,
                'name': att.name,
                'mimetype': att.mimetype,
                'placeholder': False,
                'mail_template_id': mail_template.id,
            }
            for att in mail_template.attachment_ids
        ]

    # -------------------------------------------------------------------------
    # Computed fields
    # -------------------------------------------------------------------------

    @api.depends('partner_ids')
    def _compute_mode(self):
        for wiz in self:
            wiz.mode = 'single' if len(wiz.partner_ids) == 1 else 'multi'

    @api.depends('checkbox_send_mail')
    def _compute_mail_ui_state(self):
        """Determine whether the full mail composer should be shown
        and whether the send-mail checkbox should be read-only."""
        for wiz in self:
            wiz.display_mail_composer = wiz.mode == 'single'
            recipients_missing_email = wiz.mail_partner_ids.filtered(lambda p: not p.email)
            wiz.send_mail_readonly = recipients_missing_email == wiz.mail_partner_ids

    @api.depends('mail_partner_ids', 'checkbox_send_mail', 'send_mail_readonly')
    def _compute_warnings(self):
        for wiz in self:
            alert_map = {}

            no_email_partners = wiz.mail_partner_ids.filtered(lambda p: not p.email)
            if wiz.send_mail_readonly or (wiz.checkbox_send_mail and no_email_partners):
                alert_map['account_missing_email'] = {
                    'message': _("Partner(s) should have an email address."),
                    'action_text': _("View Partner(s)"),
                    'action': no_email_partners._get_records_action(
                        name=_("Check Partner(s) Email(s)")
                    ),
                }

            wiz.warnings = alert_map

    @api.depends('partner_ids')
    def _compute_mail_lang(self):
        for wiz in self:
            if wiz.mode == 'single':
                wiz.mail_lang = wiz.partner_ids.lang
            else:
                wiz.mail_lang = get_lang(self.env).code

    @api.depends('account_report_id', 'report_options')
    def _compute_partner_ids(self):
        for wiz in self:
            wiz.partner_ids = wiz.account_report_id._get_report_send_recipients(
                wiz.report_options
            )

    @api.depends('account_report_id', 'report_options')
    def _compute_mail_partner_ids(self):
        for wiz in self:
            wiz.mail_partner_ids = wiz.partner_ids

    @api.depends('mail_template_id', 'mail_lang', 'mode')
    def _compute_mail_subject_body(self):
        for wiz in self:
            if wiz.mode == 'single' and wiz.mail_template_id:
                wiz.mail_subject = self._get_mail_field_value(
                    wiz.mail_partner_ids, wiz.mail_template_id, wiz.mail_lang, 'subject'
                )
                wiz.mail_body = self._get_mail_field_value(
                    wiz.mail_partner_ids, wiz.mail_template_id, wiz.mail_lang,
                    'body_html', options={'post_process': True},
                )
            else:
                wiz.mail_subject = wiz.mail_body = None

    @api.depends('mail_template_id', 'mode')
    def _compute_mail_attachments_widget(self):
        for wiz in self:
            if wiz.mode == 'single':
                wiz.mail_attachments_widget = wiz._get_default_mail_attachments_widget(
                    wiz.mail_partner_ids, wiz.mail_template_id,
                )
            else:
                wiz.mail_attachments_widget = []

    # -------------------------------------------------------------------------
    # Actions
    # -------------------------------------------------------------------------

    @api.model
    def _action_download(self, attachments):
        """Return an action that triggers browser download of the given attachments,
        or a zip archive when multiple files are present."""
        return {
            'type': 'ir.actions.act_url',
            'url': f'/account_reports/download_attachments/{",".join(map(str, attachments.ids))}',
            'close': True,
        }

    def _process_send_and_print(self, report, options, recipient_partner_ids=None, wizard=None):
        """Core processing logic: generates the report for each partner,
        optionally emails it, and collects attachments for download.

        :param report: account.report record to generate
        :param options: dict of report generation options
        :param recipient_partner_ids: explicit list of partner IDs to receive emails
        :param wizard: the send wizard record (None when running via cron)
        """
        stored_vals = report.send_and_print_values if not wizard else wizard._get_wizard_values()
        should_email = stored_vals['checkbox_send_mail']
        should_download = stored_vals['checkbox_download']

        template = self.env['mail.template'].browse(stored_vals['mail_template_id'])

        if wizard:
            attachment_ids_from_widget = [
                item['id']
                for item in (wizard.mail_attachments_widget or [])
                if not item['placeholder']
            ]
        else:
            attachment_ids_from_widget = template.attachment_ids.ids

        options['unfold_all'] = True

        target_partner_ids = options.get('partner_ids', [])
        target_partners = self.env['res.partner'].browse(target_partner_ids)

        if not recipient_partner_ids:
            recipient_partner_ids = target_partners.filtered('email').ids

        files_for_download = self.env['ir.attachment']

        for partner_rec in target_partners:
            options['partner_ids'] = partner_rec.ids
            generated_attachment = partner_rec._get_partner_account_report_attachment(
                report, options,
            )

            if should_email and recipient_partner_ids:
                # Determine subject/body based on single vs multi mode
                if wizard and wizard.mode == 'single':
                    email_subject = self.mail_subject
                    email_body = self.mail_body
                else:
                    email_subject = self._get_mail_field_value(
                        partner_rec, template, partner_rec.lang, 'subject',
                    )
                    email_body = self._get_mail_field_value(
                        partner_rec, template, partner_rec.lang,
                        'body_html', options={'post_process': True},
                    )

                partner_rec.message_post(
                    body=email_body,
                    subject=email_subject,
                    partner_ids=recipient_partner_ids,
                    attachment_ids=attachment_ids_from_widget + generated_attachment.ids,
                )

            if should_download:
                files_for_download += generated_attachment

        if files_for_download:
            return self._action_download(files_for_download)

    def action_send_and_print(self, force_synchronous=False):
        """Entry point: dispatch report generation, emailing, and/or downloading.

        When sending to multiple recipients without download, processing is
        deferred to a background cron job for better performance.

        :param force_synchronous: bypass async processing when True
        """
        self.ensure_one()

        if self.mode == 'multi' and self.checkbox_send_mail and not self.mail_template_id:
            raise UserError(
                _('Please select a mail template to send multiple statements.')
            )

        # Download always requires synchronous processing
        force_synchronous = force_synchronous or self.checkbox_download
        defer_to_cron = self.mode == 'multi' and not force_synchronous

        if defer_to_cron:
            if self.account_report_id.send_and_print_values:
                raise UserError(
                    _('There are currently reports waiting to be sent, please try again later.')
                )
            self.account_report_id.send_and_print_values = self._get_wizard_values()
            self.env.ref('fusion_accounting.ir_cron_account_report_send')._trigger()
            return {
                'type': 'ir.actions.client',
                'tag': 'display_notification',
                'params': {
                    'type': 'info',
                    'title': _('Sending statements'),
                    'message': _('Statements are being sent in the background.'),
                    'next': {'type': 'ir.actions.act_window_close'},
                },
            }

        merged_options = {
            **self.report_options,
            'partner_ids': self.partner_ids.ids,
        }
        return self._process_send_and_print(
            report=self.account_report_id,
            options=merged_options,
            recipient_partner_ids=self.mail_partner_ids.ids,
            wizard=self,
        )
