# -*- coding: utf-8 -*-
# Fusion PDF Template Engine
# Generic system for filling any funding agency's PDF forms

import base64
import logging
from io import BytesIO

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

_logger = logging.getLogger(__name__)


class FusionPdfTemplate(models.Model):
    _name = 'fusion.pdf.template'
    _description = 'PDF Form Template'
    _order = 'category, name'

    name = fields.Char(string='Template Name', required=True)
    category = fields.Selection([
        ('adp', 'ADP - Assistive Devices Program'),
        ('mod', 'March of Dimes'),
        ('odsp', 'ODSP'),
        ('hardship', 'Hardship Funding'),
        ('other', 'Other'),
    ], string='Funding Agency', required=True, default='adp')
    version = fields.Char(string='Form Version', default='1.0')
    state = fields.Selection([
        ('draft', 'Draft'),
        ('active', 'Active'),
        ('archived', 'Archived'),
    ], string='Status', default='draft', tracking=True)

    # The actual PDF template file
    pdf_file = fields.Binary(string='PDF Template', required=True, attachment=True)
    pdf_filename = fields.Char(string='PDF Filename')
    page_count = fields.Integer(
        string='Page Count',
        compute='_compute_page_count',
        store=True,
    )

    # Page preview images for the visual editor
    preview_ids = fields.One2many(
        'fusion.pdf.template.preview', 'template_id',
        string='Page Previews',
    )

    # Field positions configured via the visual editor
    field_ids = fields.One2many(
        'fusion.pdf.template.field', 'template_id',
        string='Template Fields',
    )
    field_count = fields.Integer(
        string='Fields',
        compute='_compute_field_count',
    )

    notes = fields.Text(
        string='Notes',
        help='Usage notes, which assessments/forms use this template',
    )

    def write(self, vals):
        res = super().write(vals)
        if 'pdf_file' in vals and vals['pdf_file']:
            for rec in self:
                try:
                    rec.action_generate_previews()
                except Exception as e:
                    _logger.warning("Auto preview generation failed for %s: %s", rec.name, e)
        return res

    @api.model_create_multi
    def create(self, vals_list):
        records = super().create(vals_list)
        for rec in records:
            if rec.pdf_file:
                try:
                    rec.action_generate_previews()
                except Exception as e:
                    _logger.warning("Auto preview generation failed for %s: %s", rec.name, e)
        return records

    @api.depends('pdf_file')
    def _compute_page_count(self):
        for rec in self:
            if rec.pdf_file:
                try:
                    from odoo.tools.pdf import PdfFileReader
                    pdf_data = base64.b64decode(rec.pdf_file)
                    reader = PdfFileReader(BytesIO(pdf_data))
                    rec.page_count = reader.getNumPages()
                except Exception as e:
                    _logger.warning("Could not read PDF page count: %s", e)
                    rec.page_count = 0
            else:
                rec.page_count = 0

    def action_generate_previews(self):
        """Generate PNG preview images from the PDF using poppler (pdftoppm).
        Falls back gracefully if the PDF is protected or poppler is not available.
        """
        self.ensure_one()
        if not self.pdf_file:
            raise UserError(_('Please upload a PDF file first.'))

        import subprocess
        import tempfile
        import os

        pdf_data = base64.b64decode(self.pdf_file)

        try:
            with tempfile.TemporaryDirectory() as tmpdir:
                pdf_path = os.path.join(tmpdir, 'template.pdf')
                with open(pdf_path, 'wb') as f:
                    f.write(pdf_data)

                # Use pdftoppm to convert each page to PNG
                result = subprocess.run(
                    ['pdftoppm', '-png', '-r', '200', pdf_path, os.path.join(tmpdir, 'page')],
                    capture_output=True, timeout=30,
                )

                if result.returncode != 0:
                    stderr = result.stderr.decode('utf-8', errors='replace')
                    _logger.warning("pdftoppm failed: %s", stderr)
                    raise UserError(_(
                        'Could not generate previews automatically. '
                        'The PDF may be protected. Please upload preview images manually '
                        'in the Page Previews tab (screenshots of each page).'
                    ))

                # Find generated PNG files
                png_files = sorted([
                    f for f in os.listdir(tmpdir)
                    if f.startswith('page-') and f.endswith('.png')
                ])

                if not png_files:
                    raise UserError(_('No pages were generated. Please upload preview images manually.'))

                # Delete existing previews
                self.preview_ids.unlink()

                # Create preview records
                for idx, png_file in enumerate(png_files):
                    png_path = os.path.join(tmpdir, png_file)
                    with open(png_path, 'rb') as f:
                        image_data = base64.b64encode(f.read())

                    self.env['fusion.pdf.template.preview'].create({
                        'template_id': self.id,
                        'page': idx + 1,
                        'image': image_data,
                        'image_filename': f'page_{idx + 1}.png',
                    })

                _logger.info("Generated %d preview images for template %s", len(png_files), self.name)

        except subprocess.TimeoutExpired:
            raise UserError(_('PDF conversion timed out. Please upload preview images manually.'))
        except FileNotFoundError:
            raise UserError(_(
                'poppler-utils (pdftoppm) is not installed on the server. '
                'Please upload preview images manually in the Page Previews tab.'
            ))

    @api.depends('field_ids')
    def _compute_field_count(self):
        for rec in self:
            rec.field_count = len(rec.field_ids)

    def action_activate(self):
        """Set template to active."""
        self.ensure_one()
        if not self.pdf_file:
            raise UserError(_('Please upload a PDF file before activating.'))
        self.state = 'active'

    def action_archive(self):
        """Archive the template."""
        self.ensure_one()
        self.state = 'archived'

    def action_reset_draft(self):
        """Reset to draft."""
        self.ensure_one()
        self.state = 'draft'

    def action_open_field_editor(self):
        """Open the visual field position editor."""
        self.ensure_one()
        return {
            'type': 'ir.actions.act_url',
            'url': f'/fusion/pdf-editor/{self.id}',
            'target': 'new',
        }

    def generate_filled_pdf(self, context_data, signatures=None):
        """Generate a filled PDF using this template and the provided data.

        Args:
            context_data: flat dict of {field_key: value}
            signatures: dict of {field_key: binary_png} for signature fields

        Returns:
            bytes of the filled PDF
        """
        self.ensure_one()
        if not self.pdf_file:
            raise UserError(_('Template has no PDF file.'))
        if self.state != 'active':
            _logger.warning("Generating PDF from non-active template %s", self.name)

        from ..utils.pdf_filler import PDFTemplateFiller

        template_bytes = base64.b64decode(self.pdf_file)

        # Build fields_by_page dict
        fields_by_page = {}
        for field in self.field_ids.filtered(lambda f: f.is_active):
            page = field.page
            if page not in fields_by_page:
                fields_by_page[page] = []
            fields_by_page[page].append({
                'field_name': field.name,
                'field_key': field.field_key or field.name,
                'pos_x': field.pos_x,
                'pos_y': field.pos_y,
                'width': field.width,
                'height': field.height,
                'field_type': field.field_type,
                'font_size': field.font_size,
                'font_name': field.font_name or 'Helvetica',
                'text_align': field.text_align or 'left',
            })

        return PDFTemplateFiller.fill_template(
            template_bytes, fields_by_page, context_data, signatures
        )


class FusionPdfTemplatePreview(models.Model):
    _name = 'fusion.pdf.template.preview'
    _description = 'PDF Template Page Preview'
    _order = 'page'

    template_id = fields.Many2one(
        'fusion.pdf.template', string='Template',
        required=True, ondelete='cascade', index=True,
    )
    page = fields.Integer(string='Page Number', required=True, default=1)
    image = fields.Binary(string='Page Image (PNG)', attachment=True)
    image_filename = fields.Char(string='Image Filename')


class FusionPdfTemplateField(models.Model):
    _name = 'fusion.pdf.template.field'
    _description = 'PDF Template Field'
    _order = 'page, sequence'

    template_id = fields.Many2one(
        'fusion.pdf.template', string='Template',
        required=True, ondelete='cascade', index=True,
    )
    name = fields.Char(
        string='Field Name', required=True,
        help='Internal identifier, e.g. client_last_name',
    )
    label = fields.Char(
        string='Display Label',
        help='Human-readable label shown in the editor, e.g. "Last Name"',
    )
    sequence = fields.Integer(string='Sequence', default=10)
    page = fields.Integer(string='Page', default=1, required=True)

    # Percentage-based positioning (0.0 to 1.0) -- same as sign.item
    pos_x = fields.Float(
        string='Position X', digits=(4, 3),
        help='Horizontal position as ratio (0.0 = left edge, 1.0 = right edge)',
    )
    pos_y = fields.Float(
        string='Position Y', digits=(4, 3),
        help='Vertical position as ratio (0.0 = top edge, 1.0 = bottom edge)',
    )
    width = fields.Float(
        string='Width', digits=(4, 3), default=0.150,
        help='Width as ratio of page width',
    )
    height = fields.Float(
        string='Height', digits=(4, 3), default=0.015,
        help='Height as ratio of page height',
    )

    # Rendering settings
    field_type = fields.Selection([
        ('text', 'Text'),
        ('checkbox', 'Checkbox'),
        ('signature', 'Signature Image'),
        ('date', 'Date'),
    ], string='Field Type', default='text', required=True)
    font_size = fields.Float(string='Font Size', default=10.0)
    font_name = fields.Selection([
        ('Helvetica', 'Helvetica'),
        ('Courier', 'Courier'),
        ('Times-Roman', 'Times Roman'),
    ], string='Font', default='Helvetica')
    text_align = fields.Selection([
        ('left', 'Left'),
        ('center', 'Center'),
        ('right', 'Right'),
    ], string='Text Alignment', default='left')

    # Data mapping
    field_key = fields.Char(
        string='Data Key',
        help='Key to look up in the data context dict.\n'
             'Examples: client_last_name, client_health_card, consent_date, signature_page_11\n'
             'The generating code passes a flat dict of all available data.',
    )
    default_value = fields.Char(
        string='Default Value',
        help='Fallback value if field_key returns empty',
    )
    is_active = fields.Boolean(string='Active', default=True)
