# -*- coding: utf-8 -*-
# Copyright 2024-2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)

from odoo import models, fields, api, _
from odoo.exceptions import UserError
import base64
import io
import os
import logging

_logger = logging.getLogger(__name__)

try:
    import pdfrw
except ImportError:
    pdfrw = None
    _logger.warning("pdfrw not installed. Discretionary Benefits PDF filling will not work.")


class DiscretionaryBenefitWizard(models.TransientModel):
    _name = 'fusion_claims.discretionary.benefit.wizard'
    _description = 'ODSP Discretionary Benefits Form Wizard'

    sale_order_id = fields.Many2one('sale.order', required=True, readonly=True)

    # --- Auto-populated from partner ---
    client_name = fields.Char(string='Client Name', readonly=True)
    address = fields.Char(string='Address', readonly=True)
    city = fields.Char(string='City', readonly=True)
    province = fields.Char(string='Province', default='ON')
    postal_code = fields.Char(string='Postal Code', readonly=True)
    phone_number = fields.Char(string='Phone', readonly=True)
    alt_phone = fields.Char(string='Alternate Phone', readonly=True)
    email = fields.Char(string='Email', readonly=True)
    member_id = fields.Char(string='ODSP Member ID', readonly=True)
    form_date = fields.Date(string='Date', default=fields.Date.today)

    odsp_office_id = fields.Many2one(
        'res.partner',
        string='ODSP Office',
        domain="[('x_fc_contact_type', '=', 'odsp_office')]",
        help='Override the ODSP office for this submission',
    )

    # --- User-editable ---
    item_type = fields.Selection([
        ('medical_equipment', 'Medical Equipment'),
        ('vision_care', 'Vision Care'),
        ('dentures', 'Dentures'),
        ('other', 'Other'),
    ], string='Item Type', default='medical_equipment', required=True)

    other_description = fields.Text(
        string='Description / Request Details',
        help='Details about the request (visible for all types, required for Other)',
    )

    email_body_notes = fields.Text(
        string='Email Body Notes',
        help='Urgency or priority notes that appear at the top of the email body, '
             'right below the title. Use this for time-sensitive requests.',
    )

    @api.model
    def default_get(self, fields_list):
        """Pre-populate from sale order and partner."""
        res = super().default_get(fields_list)
        order_id = self.env.context.get('active_id')
        if not order_id:
            return res

        order = self.env['sale.order'].browse(order_id)
        partner = order.partner_id
        res['sale_order_id'] = order.id

        if partner:
            res['client_name'] = partner.name or ''
            res['address'] = partner.street or ''
            res['city'] = partner.city or ''
            res['province'] = partner.state_id.code if partner.state_id else 'ON'
            res['postal_code'] = partner.zip or ''
            res['phone_number'] = partner.phone or ''
            res['alt_phone'] = ''
            res['email'] = partner.email or ''

        res['member_id'] = order.x_fc_odsp_member_id or (partner and partner.x_fc_odsp_member_id) or ''

        if order.x_fc_odsp_office_id:
            res['odsp_office_id'] = order.x_fc_odsp_office_id.id

        return res

    def _get_template_path(self):
        """Get the path to the Discretionary Benefits form template PDF."""
        module_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        return os.path.join(module_path, 'static', 'src', 'pdf', 'discretionary_benefits_form_template.pdf')

    def _build_field_mapping(self):
        """Build a dictionary mapping PDF field names to values."""
        self.ensure_one()
        mapping = {}

        mapping['txt_First[0]'] = self.client_name or ''
        # txt_CITY[1] is physically the Member ID field (despite the name)
        mapping['txt_CITY[1]'] = self.member_id or ''
        mapping['txt_add[0]'] = self.address or ''
        mapping['txt_CITY[0]'] = self.city or ''
        mapping['txt_prov[0]'] = self.province or 'ON'
        mapping['txt_postalcodes[0]'] = self.postal_code or ''
        # txt_email[0] is physically the Phone field (despite the name)
        mapping['txt_email[0]'] = self.phone_number or ''
        mapping['txt_bphone[0]'] = self.alt_phone or ''
        # txt_emp_phone[0] is physically the Email field (despite the name)
        mapping['txt_emp_phone[0]'] = self.email or ''
        # txt_clientnumber[0] is physically the Date field (despite the name)
        date_str = ''
        if self.form_date:
            date_str = self.form_date.strftime('%b %d, %Y')
        mapping['txt_clientnumber[0]'] = date_str

        # Item type checkboxes (mapped by physical position on the form)
        mapping['CheckBox15[0]'] = self.item_type == 'medical_equipment'
        mapping['CheckBox11[0]'] = self.item_type == 'dentures'
        mapping['CheckBox11[1]'] = self.item_type == 'vision_care'
        mapping['CheckBox13[0]'] = self.item_type == 'other'

        # Description / request details
        mapping['TextField1[0]'] = self.other_description or ''

        return mapping

    def _fill_pdf(self):
        """Fill the Discretionary Benefits PDF using PyPDF2.
        This PDF is AES-encrypted, so we use PyPDF2 which handles
        decryption and form filling natively (pdfrw cannot).
        """
        self.ensure_one()
        from PyPDF2 import PdfReader, PdfWriter
        from PyPDF2.generic import NameObject, BooleanObject

        template_path = self._get_template_path()
        if not os.path.exists(template_path):
            raise UserError(_("Discretionary Benefits form template not found at %s") % template_path)

        mapping = self._build_field_mapping()

        reader = PdfReader(template_path)
        if reader.is_encrypted:
            reader.decrypt('')

        writer = PdfWriter()
        for page in reader.pages:
            writer.add_page(page)

        # Preserve AcroForm from original
        if '/AcroForm' in reader.trailer['/Root']:
            writer._root_object[NameObject('/AcroForm')] = reader.trailer['/Root']['/AcroForm']
            writer._root_object['/AcroForm'][NameObject('/NeedAppearances')] = BooleanObject(True)

        # Split text fields and checkbox fields
        text_fields = {}
        checkbox_fields = {}
        for key, value in mapping.items():
            if isinstance(value, bool):
                checkbox_fields[key] = value
            else:
                text_fields[key] = str(value)

        # Fill text fields via PyPDF2 bulk method
        writer.update_page_form_field_values(writer.pages[0], text_fields)

        # Fill checkboxes by directly updating each annotation
        page = writer.pages[0]
        for annot_ref in page['/Annots']:
            annot = annot_ref.get_object()
            if annot.get('/FT') != '/Btn':
                continue
            field_name = str(annot.get('/T', ''))
            if field_name not in checkbox_fields:
                continue
            if checkbox_fields[field_name]:
                annot[NameObject('/V')] = NameObject('/1')
                annot[NameObject('/AS')] = NameObject('/1')
            else:
                annot[NameObject('/V')] = NameObject('/Off')
                annot[NameObject('/AS')] = NameObject('/Off')

        output = io.BytesIO()
        writer.write(output)
        return output.getvalue()

    def _sync_odsp_office(self):
        """Sync ODSP office back to sale order if changed in wizard."""
        if self.odsp_office_id and self.odsp_office_id != self.sale_order_id.x_fc_odsp_office_id:
            self.sale_order_id.x_fc_odsp_office_id = self.odsp_office_id

    def _generate_and_attach(self):
        """Generate filled PDF and quotation, attach both to sale order.
        Returns (disc_attachment, quote_attachment) ir.attachment records.
        """
        order = self.sale_order_id

        pdf_data = self._fill_pdf()
        disc_filename = f'Discretionary_Benefits_{order.name}.pdf'
        encoded_pdf = base64.b64encode(pdf_data)
        disc_attachment = self.env['ir.attachment'].create({
            'name': disc_filename,
            'type': 'binary',
            'datas': encoded_pdf,
            'res_model': 'sale.order',
            'res_id': order.id,
            'mimetype': 'application/pdf',
        })

        if order.x_fc_odsp_division == 'ontario_works':
            order.write({
                'x_fc_ow_discretionary_form': encoded_pdf,
                'x_fc_ow_discretionary_form_filename': disc_filename,
            })

        report = self.env.ref('sale.action_report_saleorder')
        quote_pdf, _ct = report._render_qweb_pdf(report.id, [order.id])
        quote_filename = f'{order.name}.pdf'
        quote_attachment = self.env['ir.attachment'].create({
            'name': quote_filename,
            'type': 'binary',
            'datas': base64.b64encode(quote_pdf),
            'res_model': 'sale.order',
            'res_id': order.id,
            'mimetype': 'application/pdf',
        })

        return disc_attachment, quote_attachment

    def action_fill_and_attach(self):
        """Fill the Discretionary Benefits PDF and attach to sale order via chatter."""
        self.ensure_one()
        self._sync_odsp_office()
        order = self.sale_order_id

        disc_att, quote_att = self._generate_and_attach()

        if order._get_odsp_status() == 'quotation':
            order._odsp_advance_status('documents_ready',
                "Documents ready after Discretionary Benefits form attached.")

        order.message_post(
            body=_("Discretionary Benefits form filled and attached."),
            message_type='comment',
            attachment_ids=[disc_att.id, quote_att.id],
        )

        return {'type': 'ir.actions.act_window_close'}

    def _advance_status_on_submit(self, order):
        """Advance status when form is submitted (sent via email/fax)."""
        current = order._get_odsp_status()
        if order.x_fc_odsp_division == 'ontario_works' and current in ('quotation', 'documents_ready'):
            order._odsp_advance_status('submitted_to_ow',
                "Discretionary Benefits form submitted to Ontario Works.")
        elif current == 'quotation':
            order._odsp_advance_status('documents_ready',
                "Documents ready after Discretionary Benefits form attached.")

    def action_send_fax(self):
        """Fill form, generate quotation, and open the fax wizard with both attached.
        Chatter posting is handled by the fax module when the fax is actually sent.
        """
        self.ensure_one()
        self._sync_odsp_office()
        order = self.sale_order_id
        office = self.odsp_office_id or order.x_fc_odsp_office_id

        disc_att, quote_att = self._generate_and_attach()

        self._advance_status_on_submit(order)

        ctx = {
            'default_sale_order_id': order.id,
            'default_partner_id': office.id if office else False,
            'default_generate_pdf': False,
            'forward_attachment_ids': [disc_att.id, quote_att.id],
        }
        if office and hasattr(office, 'x_ff_fax_number') and office.x_ff_fax_number:
            ctx['default_fax_number'] = office.x_ff_fax_number

        return {
            'type': 'ir.actions.act_window',
            'name': _('Send Fax - Discretionary Benefits'),
            'res_model': 'fusion_faxes.send.fax.wizard',
            'view_mode': 'form',
            'target': 'new',
            'context': ctx,
        }

    def action_send_email(self):
        """Fill form, generate quotation, and email both to ODSP office."""
        self.ensure_one()
        self._sync_odsp_office()
        order = self.sale_order_id
        office = self.odsp_office_id or order.x_fc_odsp_office_id

        if not office or not office.email:
            raise UserError(_(
                "No ODSP Office with an email address is set. "
                "Please select an ODSP Office before sending."
            ))

        disc_att, quote_att = self._generate_and_attach()

        order._send_odsp_submission_email(
            attachment_ids=[disc_att.id, quote_att.id],
            email_body_notes=self.email_body_notes,
        )

        self._advance_status_on_submit(order)

        order.message_post(
            body=_("Discretionary Benefits form and quotation emailed to %s.") % office.name,
            message_type='comment',
            attachment_ids=[disc_att.id, quote_att.id],
        )

        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Email Sent'),
                'message': _('Discretionary Benefits form emailed to %s.') % office.email,
                'type': 'success',
                'sticky': False,
                'next': {'type': 'ir.actions.act_window_close'},
            },
        }

    def action_send_fax_and_email(self):
        """Fill form, generate quotation, send email, then open fax wizard.
        Email is sent first with a notification, then fax wizard opens.
        """
        self.ensure_one()
        self._sync_odsp_office()
        order = self.sale_order_id
        office = self.odsp_office_id or order.x_fc_odsp_office_id

        if not office or not office.email:
            raise UserError(_(
                "No ODSP Office with an email address is set. "
                "Please select an ODSP Office before sending."
            ))

        disc_att, quote_att = self._generate_and_attach()

        order._send_odsp_submission_email(
            attachment_ids=[disc_att.id, quote_att.id],
            email_body_notes=self.email_body_notes,
        )

        self._advance_status_on_submit(order)

        order.message_post(
            body=_("Discretionary Benefits form and quotation emailed to %s.") % office.name,
            message_type='comment',
            attachment_ids=[disc_att.id, quote_att.id],
        )

        ctx = {
            'default_sale_order_id': order.id,
            'default_partner_id': office.id,
            'default_generate_pdf': False,
            'forward_attachment_ids': [disc_att.id, quote_att.id],
        }
        if hasattr(office, 'x_ff_fax_number') and office.x_ff_fax_number:
            ctx['default_fax_number'] = office.x_ff_fax_number

        fax_action = {
            'type': 'ir.actions.act_window',
            'name': _('Send Fax - Discretionary Benefits'),
            'res_model': 'fusion_faxes.send.fax.wizard',
            'view_mode': 'form',
            'target': 'new',
            'context': ctx,
        }

        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Email Sent'),
                'message': _('Email sent to %s. Now proceeding to fax...') % office.email,
                'type': 'success',
                'sticky': False,
                'next': fax_action,
            },
        }
