# Copyright © 2023 Garazd Creation (https://garazd.biz)
# @author: Yurii Razumovskyi (support@garazd.biz)
# @author: Iryna Razumovska (support@garazd.biz)
# License OPL-1 (https://www.odoo.com/documentation/15.0/legal/licenses.html).

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


class PrintProductLabelTemplate(models.Model):
    _name = "print.product.label.template"
    _description = 'Product Label Templates'
    _order = 'sequence'

    def _default_sequence(self):
        return (self.search([], order="sequence desc", limit=1).sequence or 0) + 1

    sequence = fields.Integer(default=_default_sequence)
    name = fields.Char(required=True)
    type_id = fields.Many2one(
        comodel_name='print.label.type',
        default=lambda self: self.env.ref('garazd_product_label.type_product'),
        help='You can specify a type of this label. It makes sense if you use '
             'additional extensions to print labels not for products only but for other objects as well. '
             'Like as Stock Packages, Sales Orders, Manufacturing Orders, etc. '
             'By this type, you can filter your label templates in the print wizard.',
        ondelete='set null',
        required=False,
    )
    section_ids = fields.One2many(
        comodel_name='print.product.label.section',
        inverse_name='template_id',
        string='Sections',
    )
    section_count = fields.Integer(compute='_compute_section_count')
    paperformat_id = fields.Many2one(
        comodel_name='report.paperformat',
        # All label paperformats should have prefix "Label: "
        domain="[('name', 'like', 'Label: %')]",
        readonly=True,
        required=True,
    )
    format = fields.Selection(related='paperformat_id.format', store=True)
    orientation = fields.Selection(
        related='paperformat_id.orientation',
        readonly=False,
        required=True,
        help='Page Orientation. Only system administrators can change this value.',
    )
    rows = fields.Integer(default=1, required=True)
    cols = fields.Integer(default=1, required=True)
    margin_top = fields.Float(
        related='paperformat_id.margin_top',
        help='Page Top Margin in mm. Only system administrators can change this value.',
        readonly=False,
    )
    margin_bottom = fields.Float(
        related='paperformat_id.margin_bottom',
        help='Page Bottom Margin in mm. '
             'Only system administrators can change this value.',
        readonly=False,
    )
    margin_left = fields.Float(
        related='paperformat_id.margin_left',
        help='Page Left Margin in mm. Only system administrators can change this value.',
        readonly=False,
    )
    margin_right = fields.Float(
        related='paperformat_id.margin_right',
        help='Page Right Margin in mm. '
             'Only system administrators can change this value.',
        readonly=False,
    )
    padding_top = fields.Float(
        default=0, digits=(10, 2), help='Label Right Padding in mm.')
    padding_bottom = fields.Float(
        default=0, digits=(10, 2), help='Label Bottom Padding in mm.')
    padding_left = fields.Float(
        default=0, digits=(10, 2), help='Label Left Padding in mm.')
    padding_right = fields.Float(
        default=0, digits=(10, 2), help='Label Right Padding in mm.')
    label_style = fields.Char(string='Custom Label Style')
    width = fields.Float(digits=(10, 2), help='Label Width in mm.')
    height = fields.Float(digits=(10, 2), help='Label Height in mm.')
    row_gap = fields.Float(
        string='Horizontal',
        digits=(10, 2),
        default=0,
        help='Horizontal gap between labels, in mm.',
    )
    col_gap = fields.Float(
        string='Vertical',
        digits=(10, 2),
        default=0,
        help='Vertical gap between labels, in mm.',
    )
    is_oversized = fields.Boolean(compute='_compute_is_oversized')
    description = fields.Char()
    preview = fields.Boolean(default=True, help='Show the sample label.')
    preview_html = fields.Html(compute='_compute_preview_html', compute_sudo=True)
    ratio_px_in_mm = fields.Float(
        string='Ratio (px in mm)',
        digits=(10, 4),
        compute='_compute_ratio_px_in_mm',
        help="Technical field that indicates how many pixels in 1 mm.",
        store=True,
    )
    active = fields.Boolean(default=True)

    @api.depends('section_ids')
    def _compute_section_count(self):
        for template in self:
            template.section_count = len(template.section_ids)

    @api.depends('width', 'height', 'section_ids', 'section_ids.height')
    def _compute_is_oversized(self):
        for template in self:
            total_height = sum(template.section_ids.mapped('height')) \
                + template.padding_top + template.padding_bottom
            template.is_oversized = template.height < total_height

    @api.depends('paperformat_id', 'paperformat_id.dpi')
    def _compute_ratio_px_in_mm(self):
        for template in self:
            template.ratio_px_in_mm = template.paperformat_id.dpi / 25.4

    def _set_paperformat(self):
        self.ensure_one()
        self.env.ref(
            'garazd_product_label.action_report_product_label_from_template'
        ).sudo().paperformat_id = self.paperformat_id.id

    def write(self, vals):
        """If the Dymo label width or height were changed,
        we should change it to the related paperformat."""
        res = super(PrintProductLabelTemplate, self).write(vals)
        if 'width' in vals or 'height' in vals:
            for template in self:
                if template.paperformat_id.format == 'custom' \
                        and template.cols == 1 and template.rows == 1:
                    template.paperformat_id.sudo().write({
                        # flake8: noqa: E501
                        'page_width': float_round(template.width, precision_rounding=1, rounding_method='UP'),
                        'page_height': float_round(template.height, precision_rounding=1, rounding_method='UP'),
                    })
        return res

    def unlink(self):
        paperformats = self.mapped('paperformat_id')
        res = super(PrintProductLabelTemplate, self).unlink()
        paperformats.sudo().unlink()
        return res

    def get_demo_product(self):
        self.ensure_one()
        product = self.env['product.product'].browse()

        # If the label template is opened from the print wizard (the 'print_wizard_id' value in the context),
        # use the first product from the list. Otherwise, use the demo product

        # Case 1: Get a real product to display
        if self.env.company.print_label_preview_type == 'real_product':
            if self._context.get('print_product_id'):
                product = self.env['product.product'].browse(self._context.get('print_product_id'))
            if not product and self._context.get('print_wizard_id'):
                wizard = self.env['print.product.label'].browse(self._context.get('print_wizard_id'))
                if wizard.label_ids:
                    product = wizard.label_ids[0].product_id

        # Case 2: Get a demo product that is specified in the general settings
        if not product:
            product = self.env.company.print_label_preview_product_id

        return product

    def get_demo_product_label(self, product: models.Model = None):
        self.ensure_one()
        PrintWizard = self.env['print.product.label']
        ProductLabel = self.env['print.product.label.line']
        wizard = PrintWizard.browse()
        label = ProductLabel.browse()

        # Get a real product to display
        # If the label template is opened from the print wizard (the 'print_wizard_id' value in the context),
        # use the first product from the list. Otherwise, use the demo product
        if self.env.company.print_label_preview_type == 'real_product' and self._context.get('print_wizard_id'):
            wizard = PrintWizard.browse(self._context.get('print_wizard_id'))
            label = wizard.label_ids[:1]

        demo_product = product or self.get_demo_product()
        if not demo_product:
            raise UserError(_("Please select a demo product in the General Settings."))

        if not wizard or not label:
            pricelist_id = self._context.get('pricelist_id')
            pricelist = self.env['product.pricelist'].browse(pricelist_id) \
                if pricelist_id else self.env.company.print_label_preview_pricelist_id
            sale_pricelist_id = self._context.get('sale_pricelist_id')
            sale_pricelist = self.env['product.pricelist'].browse(sale_pricelist_id) \
                if sale_pricelist_id else self.env.company.print_label_preview_sale_pricelist_id
            wizard = PrintWizard.create({
                'template_id': self.id,
                'company_id': self.env.company.id,
                'pricelist_id': pricelist.id,
                'sale_pricelist_id': sale_pricelist.id,
            })
            label = ProductLabel.create({
                'wizard_id': wizard.id,
                'product_id': demo_product.id,
                'price': demo_product.lst_price,
                'barcode': demo_product.barcode,
            })
        return label

    @api.depends('preview')
    def _compute_preview_html(self):
        for template in self:
            demo_product = template.get_demo_product()
            template.preview_html = template.get_preview_html() if demo_product else ''

    def get_preview_html(self):
        self.ensure_one()
        values = {
            'back_style':
                'background-color: #CCCCCC; '
                'width: 100%; height: 100%; '
                'padding: 15px; '
                'overflow: hidden;',
            'label_style':
                'width: %(width)fmm; '
                'height: %(height)fmm; '
                'background-color: #FFFFFF; '
                'margin: auto; '
                'padding: %(padding_top)fmm '
                '%(padding_right)fmm '
                '%(padding_bottom)fmm '
                '%(padding_left)fmm; '
                '%(label_custom_style)s' % {
                    'width': self.width,
                    'height': self.height,
                    'padding_top': self.padding_top,
                    'padding_right': self.padding_right,
                    'padding_bottom': self.padding_bottom,
                    'padding_left': self.padding_left,
                    'label_custom_style': self.label_style or '',
                },
            'sections': self.section_ids.filtered('active'),
            'label': self.get_demo_product_label(),
            'editable_section_id': self._context.get('editable_section_id', False),
        }
        return self.env['ir.ui.view']._render_template('garazd_product_label_pro.label_preview', values)

    @api.depends_context('uid')
    def _get_user_allowed_templates(self):
        """
        System administrators are not restricted anyway.
        Other users are restricted if allowed templates are specified in their settings,
        otherwise these users can use all templates.
        """
        all_templates = self.search([])
        return all_templates if self.env.user.has_group('base.group_system') \
            else all_templates if not self.env.user.print_label_allowed_template_ids \
            else self.env.user.print_label_allowed_template_ids

    def action_pdf_preview(self):
        self.ensure_one()
        label = self.get_demo_product_label()
        return self.env['print.product.label']._pdf_preview(label.wizard_id.get_pdf())
