# -*- 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. SA Mobility PDF filling will not work.")


class SAMobilityPartLine(models.TransientModel):
    _name = 'fusion_claims.sa.mobility.part.line'
    _description = 'SA Mobility Parts Line'
    _order = 'sequence'

    wizard_id = fields.Many2one('fusion_claims.sa.mobility.wizard', ondelete='cascade')
    sequence = fields.Integer(default=10)
    qty = fields.Float(string='Qty', digits=(12, 2))
    description = fields.Char(string='Description')
    unit_price = fields.Float(string='Unit Price', digits=(12, 2))
    tax_id = fields.Many2one('account.tax', string='Tax Type',
                             domain=[('type_tax_use', '=', 'sale')])
    taxes = fields.Float(string='Taxes', digits=(12, 2),
                         compute='_compute_taxes', store=True)
    amount = fields.Float(string='Amount', compute='_compute_amount', store=True)

    @api.depends('qty', 'unit_price', 'tax_id')
    def _compute_taxes(self):
        for line in self:
            subtotal = line.qty * line.unit_price
            line.taxes = subtotal * line.tax_id.amount / 100 if line.tax_id else 0.0

    @api.depends('qty', 'unit_price', 'taxes')
    def _compute_amount(self):
        for line in self:
            line.amount = (line.qty * line.unit_price) + line.taxes


class SAMobilityLabourLine(models.TransientModel):
    _name = 'fusion_claims.sa.mobility.labour.line'
    _description = 'SA Mobility Labour Line'
    _order = 'sequence'

    wizard_id = fields.Many2one('fusion_claims.sa.mobility.wizard', ondelete='cascade')
    sequence = fields.Integer(default=10)
    hours = fields.Float(string='Hours', digits=(12, 2))
    rate = fields.Float(string='Rate', digits=(12, 2))
    tax_id = fields.Many2one('account.tax', string='Tax Type',
                             domain=[('type_tax_use', '=', 'sale')])
    taxes = fields.Float(string='Taxes', digits=(12, 2),
                         compute='_compute_taxes', store=True)
    amount = fields.Float(string='Amount', compute='_compute_amount', store=True)

    @api.depends('hours', 'rate', 'tax_id')
    def _compute_taxes(self):
        for line in self:
            subtotal = line.hours * line.rate
            line.taxes = subtotal * line.tax_id.amount / 100 if line.tax_id else 0.0

    @api.depends('hours', 'rate', 'taxes')
    def _compute_amount(self):
        for line in self:
            line.amount = (line.hours * line.rate) + line.taxes


class SAMobilityFeeLine(models.TransientModel):
    _name = 'fusion_claims.sa.mobility.fee.line'
    _description = 'SA Mobility Additional Fee Line'
    _order = 'sequence'

    wizard_id = fields.Many2one('fusion_claims.sa.mobility.wizard', ondelete='cascade')
    sequence = fields.Integer(default=10)
    description = fields.Char(string='Description')
    rate = fields.Float(string='Rate', digits=(12, 2))
    tax_id = fields.Many2one('account.tax', string='Tax Type',
                             domain=[('type_tax_use', '=', 'sale')])
    taxes = fields.Float(string='Taxes', digits=(12, 2),
                         compute='_compute_taxes', store=True)
    amount = fields.Float(string='Amount', compute='_compute_amount', store=True)

    @api.depends('rate', 'tax_id')
    def _compute_taxes(self):
        for line in self:
            line.taxes = line.rate * line.tax_id.amount / 100 if line.tax_id else 0.0

    @api.depends('rate', 'taxes')
    def _compute_amount(self):
        for line in self:
            line.amount = line.rate + line.taxes


class SAMobilityWizard(models.TransientModel):
    _name = 'fusion_claims.sa.mobility.wizard'
    _description = 'SA Mobility Form Filling Wizard'

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

    # --- Vendor section (auto-populated, read-only) ---
    vendor_name = fields.Char(string='Vendor Name', readonly=True)
    order_number = fields.Char(string='Order #', readonly=True)
    vendor_address = fields.Char(string='Vendor Address', readonly=True)
    primary_email = fields.Char(string='Primary Email', readonly=True)
    phone = fields.Char(string='Phone', readonly=True)
    secondary_email = fields.Char(string='Secondary Email', readonly=True)
    form_date = fields.Date(string='Date', default=fields.Date.today)

    # --- Salesperson ---
    salesperson_name = fields.Char(string='Salesperson/Technician', readonly=True)
    service_date = fields.Date(string='Date of Service', default=fields.Date.today)

    # --- Client section (auto-populated, read-only) ---
    client_last_name = fields.Char(string='Last Name', readonly=True)
    client_first_name = fields.Char(string='First Name', readonly=True)
    member_id = fields.Char(string='ODSP Member ID', size=9, readonly=True)
    client_address = fields.Char(string='Client Address', readonly=True)
    client_phone = fields.Char(string='Client Phone', readonly=True)

    # --- User-editable fields ---
    relationship = fields.Selection([
        ('self', 'Self'),
        ('spouse', 'Spouse'),
        ('dependent', 'Dependent'),
    ], string='Relationship to Recipient', default='self', required=True)

    device_type = fields.Selection([
        ('manual_wheelchair', 'Manual Wheelchair'),
        ('high_tech_wheelchair', 'High Technology Wheelchair'),
        ('mobility_scooter', 'Mobility Scooter'),
        ('walker', 'Walker'),
        ('lifting_device', 'Lifting Device'),
        ('other', 'Other'),
    ], string='Device Type', required=True)
    device_other_description = fields.Char(
        string='Other Device Description',
        help='e.g. power chair, batteries, stairlift, ceiling lift',
    )

    serial_number = fields.Char(string='Serial Number')
    year = fields.Char(string='Year')
    make = fields.Char(string='Make')
    model_name = fields.Char(string='Model')

    warranty_in_effect = fields.Boolean(string='Warranty in Effect')
    warranty_description = fields.Char(string='Warranty Description')
    after_hours = fields.Boolean(string='After-hours/Weekend Work')

    notes = fields.Text(
        string='Notes / Comments',
        help='Additional details about the request (filled into Notes/Comments area on Page 2)',
    )

    sa_request_type = fields.Selection([
        ('batteries', 'Batteries'),
        ('repair', 'Repair / Maintenance'),
    ], string='Request Type', required=True, default='repair',
       help='Controls email body template when sending to SA Mobility')

    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.',
    )

    # --- Line items ---
    part_line_ids = fields.One2many(
        'fusion_claims.sa.mobility.part.line', 'wizard_id', string='Parts')
    labour_line_ids = fields.One2many(
        'fusion_claims.sa.mobility.labour.line', 'wizard_id', string='Labour')
    fee_line_ids = fields.One2many(
        'fusion_claims.sa.mobility.fee.line', 'wizard_id', string='Additional Fees')

    # --- Computed totals ---
    parts_total = fields.Float(compute='_compute_totals', string='Parts Total')
    labour_total = fields.Float(compute='_compute_totals', string='Labour Total')
    fees_total = fields.Float(compute='_compute_totals', string='Fees Total')
    grand_total = fields.Float(compute='_compute_totals', string='Grand Total')

    @api.depends('part_line_ids.amount', 'labour_line_ids.amount', 'fee_line_ids.amount')
    def _compute_totals(self):
        for wiz in self:
            wiz.parts_total = sum(wiz.part_line_ids.mapped('amount'))
            wiz.labour_total = sum(wiz.labour_line_ids.mapped('amount'))
            wiz.fees_total = sum(wiz.fee_line_ids.mapped('amount'))
            wiz.grand_total = wiz.parts_total + wiz.labour_total + wiz.fees_total

    @api.model
    def default_get(self, fields_list):
        """Pre-populate wizard from sale order context."""
        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)
        company = order.company_id or self.env.company

        # Vendor info
        res['sale_order_id'] = order.id
        res['vendor_name'] = company.name or ''
        res['order_number'] = order.name or ''
        addr_parts = [company.street or '', company.city or '', company.zip or '']
        res['vendor_address'] = ', '.join(p for p in addr_parts if p)
        sa_email = self.env['ir.config_parameter'].sudo().get_param(
            'fusion_claims.sa_mobility_email', 'samobility@ontario.ca')
        res['primary_email'] = company.email or sa_email
        res['phone'] = company.phone or ''
        res['secondary_email'] = order.user_id.email or ''

        # Salesperson
        res['salesperson_name'] = order.user_id.name or ''

        # Client info
        partner = order.partner_id
        if partner:
            name_parts = (partner.name or '').split(' ', 1)
            res['client_first_name'] = name_parts[0] if name_parts else ''
            res['client_last_name'] = name_parts[1] if len(name_parts) > 1 else ''
            addr_parts = [partner.street or '', partner.city or '', partner.zip or '']
            res['client_address'] = ', '.join(p for p in addr_parts if p)
            res['client_phone'] = partner.phone or ''
            res['member_id'] = order.x_fc_odsp_member_id or partner.x_fc_odsp_member_id or ''

        # Restore saved device/form data from sale order (if previously filled)
        if order.x_fc_sa_device_type:
            res['relationship'] = order.x_fc_sa_relationship or 'self'
            res['device_type'] = order.x_fc_sa_device_type
            res['device_other_description'] = order.x_fc_sa_device_other or ''
            res['serial_number'] = order.x_fc_sa_serial_number or ''
            res['year'] = order.x_fc_sa_year or ''
            res['make'] = order.x_fc_sa_make or ''
            res['model_name'] = order.x_fc_sa_model or ''
            res['warranty_in_effect'] = order.x_fc_sa_warranty
            res['warranty_description'] = order.x_fc_sa_warranty_desc or ''
            res['after_hours'] = order.x_fc_sa_after_hours
            res['sa_request_type'] = order.x_fc_sa_request_type or 'repair'
            res['notes'] = order.x_fc_sa_notes or ''

        # Pre-populate parts and labour from order lines
        part_lines = []
        labour_lines = []
        part_seq = 10
        labour_seq = 10
        for line in order.order_line.filtered(lambda l: not l.display_type):
            tax = line.tax_ids[:1]
            # Route LABOR product to labour tab
            if line.product_id and line.product_id.default_code == 'LABOR':
                labour_lines.append((0, 0, {
                    'sequence': labour_seq,
                    'hours': line.product_uom_qty,
                    'rate': line.price_unit,
                    'tax_id': tax.id if tax else False,
                }))
                labour_seq += 10
            else:
                part_lines.append((0, 0, {
                    'sequence': part_seq,
                    'qty': line.product_uom_qty,
                    'description': line.product_id.name or line.name or '',
                    'unit_price': line.price_unit,
                    'tax_id': tax.id if tax else False,
                }))
                part_seq += 10
        if part_lines:
            res['part_line_ids'] = part_lines[:6]
        if labour_lines:
            res['labour_line_ids'] = labour_lines[:5]

        return res

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

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

        # Vendor section
        mapping['Text 1'] = self.vendor_name or ''
        mapping['Text2'] = self.order_number or ''
        mapping['Text3'] = self.vendor_address or ''
        mapping['Text4'] = self.primary_email or ''
        mapping['Text5'] = self.phone or ''
        mapping['Text6'] = self.secondary_email or ''
        mapping['Text7'] = fields.Date.to_string(self.form_date) if self.form_date else ''

        # Salesperson
        mapping['Text8'] = self.salesperson_name or ''
        mapping['Text9'] = fields.Date.to_string(self.service_date) if self.service_date else ''

        # Client
        mapping['Text10'] = self.client_last_name or ''
        mapping['Text11'] = self.client_first_name or ''

        # Member ID - 9 individual digit boxes (Text12-Text20)
        member = (self.member_id or '').ljust(9)
        for i in range(9):
            mapping[f'Text{12 + i}'] = member[i] if i < len(self.member_id or '') else ''

        mapping['Text21'] = self.client_address or ''
        mapping['Text22'] = self.client_phone or ''

        # Relationship checkboxes
        mapping['Check Box16'] = self.relationship == 'self'
        mapping['Check Box17'] = self.relationship == 'spouse'
        mapping['Check Box18'] = self.relationship == 'dependent'

        # Device type checkboxes
        device_map = {
            'manual_wheelchair': 'Check Box19',
            'high_tech_wheelchair': 'Check Box20',
            'mobility_scooter': 'Check Box21',
            'walker': 'Check Box22',
            'lifting_device': 'Check Box23',
            'other': 'Check Box24',
        }
        for dtype, cb_name in device_map.items():
            mapping[cb_name] = self.device_type == dtype

        mapping['Text23'] = self.device_other_description or ''
        mapping['Text24'] = self.serial_number or ''
        mapping['Text25'] = self.year or ''
        mapping['Text26'] = self.make or ''
        mapping['Text27'] = self.model_name or ''

        # Warranty
        mapping['Check Box26'] = bool(self.warranty_in_effect)
        mapping['Check Box28'] = not self.warranty_in_effect
        mapping['Text28'] = self.warranty_description or ''

        # After hours
        mapping['Check Box27'] = bool(self.after_hours)
        mapping['Check Box29'] = not self.after_hours

        # Parts lines (up to 6 rows): Text30-Text59
        for idx, line in enumerate(self.part_line_ids[:6]):
            base = 30 + (idx * 5)
            mapping[f'Text{base}'] = str(int(line.qty)) if line.qty == int(line.qty) else str(line.qty)
            mapping[f'Text{base + 1}'] = line.description or ''
            mapping[f'Text{base + 2}'] = f'${line.unit_price:.2f}'
            mapping[f'Text{base + 3}'] = f'${line.taxes:.2f}' if line.taxes else ''
            mapping[f'Text{base + 4}'] = f'${line.amount:.2f}'
        mapping['Text60'] = f'${self.parts_total:.2f}'

        # Labour lines (up to 5 rows): Text61-Text80
        for idx, line in enumerate(self.labour_line_ids[:5]):
            base = 61 + (idx * 4)
            mapping[f'Text{base}'] = str(line.hours)
            mapping[f'Text{base + 1}'] = f'${line.rate:.2f}'
            mapping[f'Text{base + 2}'] = f'${line.taxes:.2f}' if line.taxes else ''
            mapping[f'Text{base + 3}'] = f'${line.amount:.2f}'
        mapping['Text81'] = f'${self.labour_total:.2f}'

        # Additional fees (up to 4 rows): Text82-Text97
        for idx, line in enumerate(self.fee_line_ids[:4]):
            base = 82 + (idx * 4)
            mapping[f'Text{base}'] = line.description or ''
            mapping[f'Text{base + 1}'] = f'${line.rate:.2f}'
            mapping[f'Text{base + 2}'] = f'${line.taxes:.2f}' if line.taxes else ''
            mapping[f'Text{base + 3}'] = f'${line.amount:.2f}'
        mapping['Text98'] = f'${self.fees_total:.2f}'

        # Estimated totals summary
        mapping['Text99'] = f'${self.parts_total:.2f}'
        mapping['Text100'] = f'${self.labour_total:.2f}'
        mapping['Text101'] = f'${self.fees_total:.2f}'
        mapping['Text102'] = f'${self.grand_total:.2f}'

        # Page 2 - Notes/Comments area
        mapping['Text1'] = self.notes or ''

        return mapping

    def _fill_pdf(self):
        """Fill the SA Mobility PDF template using pdfrw AcroForm field filling."""
        self.ensure_one()
        if not pdfrw:
            raise UserError(_("pdfrw library is not installed. Cannot fill PDF forms."))

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

        mapping = self._build_field_mapping()
        template = pdfrw.PdfReader(template_path)

        for page in template.pages:
            annotations = page.get('/Annots')
            if not annotations:
                continue
            for annot in annotations:
                if annot.get('/Subtype') != '/Widget':
                    continue
                field_name = annot.get('/T')
                if not field_name:
                    continue
                # pdfrw wraps field names in parentheses
                clean_name = field_name.strip('()')
                if clean_name not in mapping:
                    continue

                value = mapping[clean_name]
                if isinstance(value, bool):
                    # Checkbox field
                    if value:
                        annot.update(pdfrw.PdfDict(
                            V=pdfrw.PdfName('Yes'),
                            AS=pdfrw.PdfName('Yes'),
                        ))
                    else:
                        annot.update(pdfrw.PdfDict(
                            V=pdfrw.PdfName('Off'),
                            AS=pdfrw.PdfName('Off'),
                        ))
                else:
                    # Text field
                    annot.update(pdfrw.PdfDict(
                        V=pdfrw.PdfString.encode(str(value)),
                        AP='',
                    ))

        # Mark form as not needing appearance regeneration
        if template.Root.AcroForm:
            template.Root.AcroForm.update(pdfrw.PdfDict(NeedAppearances=pdfrw.PdfObject('true')))

        output = io.BytesIO()
        writer = pdfrw.PdfWriter()
        writer.trailer = template
        writer.write(output)
        return output.getvalue()

    def _save_form_data(self):
        """Persist user-editable wizard data to sale order for future sessions."""
        self.ensure_one()
        self.sale_order_id.with_context(skip_all_validations=True).write({
            'x_fc_sa_relationship': self.relationship,
            'x_fc_sa_device_type': self.device_type,
            'x_fc_sa_device_other': self.device_other_description or '',
            'x_fc_sa_serial_number': self.serial_number or '',
            'x_fc_sa_year': self.year or '',
            'x_fc_sa_make': self.make or '',
            'x_fc_sa_model': self.model_name or '',
            'x_fc_sa_warranty': self.warranty_in_effect,
            'x_fc_sa_warranty_desc': self.warranty_description or '',
            'x_fc_sa_after_hours': self.after_hours,
            'x_fc_sa_request_type': self.sa_request_type,
            'x_fc_sa_notes': self.notes or '',
        })

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

        self._save_form_data()
        pdf_data = self._fill_pdf()
        filename = f'SA_Mobility_Form_{order.name}.pdf'

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

        # Update ODSP status if appropriate
        if order.x_fc_sa_status == 'quotation':
            order.x_fc_sa_status = 'form_ready'

        order.message_post(
            body=_("SA Mobility form filled and attached."),
            message_type='comment',
            attachment_ids=[attachment.id],
        )

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

    def action_fill_attach_and_send(self):
        """Fill PDF, attach to order, and send email to SA Mobility."""
        self.ensure_one()
        order = self.sale_order_id

        self._save_form_data()
        pdf_data = self._fill_pdf()
        filename = f'SA_Mobility_Form_{order.name}.pdf'

        # Attach to sale order
        attachment = self.env['ir.attachment'].create({
            'name': filename,
            'type': 'binary',
            'datas': base64.b64encode(pdf_data),
            'res_model': 'sale.order',
            'res_id': order.id,
            'mimetype': 'application/pdf',
        })

        # Generate quotation PDF and attach
        att_ids = [attachment.id]
        try:
            report = self.env.ref('sale.action_report_saleorder')
            pdf_content, _ct = report._render_qweb_pdf(report.id, [order.id])
            quot_att = self.env['ir.attachment'].create({
                'name': f'Quotation_{order.name}.pdf',
                'type': 'binary',
                'datas': base64.b64encode(pdf_content),
                'res_model': 'sale.order',
                'res_id': order.id,
                'mimetype': 'application/pdf',
            })
            att_ids.append(quot_att.id)
        except Exception as e:
            _logger.warning(f"Could not generate quotation PDF for {order.name}: {e}")

        # Build and send email
        order._send_sa_mobility_email(
            request_type=self.sa_request_type,
            device_description=self._get_device_label(),
            attachment_ids=att_ids,
            email_body_notes=self.email_body_notes,
        )

        # Update ODSP status
        if order.x_fc_sa_status in ('quotation', 'form_ready'):
            order.x_fc_sa_status = 'submitted_to_sa'

        sa_email = order._get_sa_mobility_email()
        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': _('Email Sent'),
                'message': _('SA Mobility form emailed to %s.') % sa_email,
                'type': 'success',
                'sticky': False,
                'next': {'type': 'ir.actions.act_window_close'},
            },
        }

    def _get_device_label(self):
        """Get human-readable device description."""
        self.ensure_one()
        labels = dict(self._fields['device_type'].selection)
        label = labels.get(self.device_type, '')
        if self.device_type == 'other' and self.device_other_description:
            label = self.device_other_description
        return label
