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

import logging
from odoo import models, fields, api

_logger = logging.getLogger(__name__)

CASE_CONVERSION_OPTIONS = [
    ('none', 'No Conversion'),
    ('upper', 'UPPERCASE'),
    ('sentence', 'Sentence case'),
    ('capitalized', 'Capitalized Case'),
    ('lower', 'lowercase'),
]


class ProductTemplate(models.Model):
    _inherit = 'product.template'

    # ── Inventory Sync (from fusion_inventory_sync) ──

    sync_mapping_ids = fields.One2many(
        'fusion.product.sync.mapping', 'local_product_id',
        string='Remote Inventory Links')
    remote_qty_available = fields.Float(
        string='Remote On Hand',
        compute='_compute_remote_stock',
        help='Total on-hand quantity at remote locations')
    remote_qty_forecast = fields.Float(
        string='Remote Forecast',
        compute='_compute_remote_stock',
        help='Total forecasted quantity at remote locations')
    has_remote_mapping = fields.Boolean(
        string='Has Remote Link',
        compute='_compute_remote_stock')

    # ── Margin / Profit ──

    x_fi_margin_pct = fields.Float(
        string='Margin (%)',
        compute='_compute_margin_pct',
        inverse='_inverse_margin_pct',
        store=True, readonly=False,
        help='Profit margin as percentage of sale price. '
             'Entering a margin auto-calculates the sale price; '
             'entering a sale price auto-calculates the margin.')
    x_fi_profit_amount = fields.Float(
        string='Profit',
        compute='_compute_profit',
        help='Sale Price minus Cost (at the template level).')
    x_fi_shipping_cost = fields.Float(
        string='Shipping Cost',
        default=0.0,
        help='Per-unit shipping cost. Saved here and automatically '
             'distributed to all variants on save.')

    # ── Brand / Vendor ──

    x_fi_brand_ids = fields.Many2many(
        'product.brand', 'product_template_brand_rel',
        'product_tmpl_id', 'brand_id',
        string='Brand(s)')
    x_fi_expected_cost = fields.Float(
        string='Expected Cost',
        compute='_compute_expected_cost',
        help='Estimated purchase cost calculated from Sales Price '
             'and the primary brand discount tiers.')

    # ── Case Conversion ──

    x_fi_case_conversion = fields.Selection(
        CASE_CONVERSION_OPTIONS,
        string='Name Case',
        default='none',
        help='Convert this product name to the selected case. '
             'Global setting in Inventory Settings overrides this.')

    # ── Purchase History (computed link to vendor bill lines) ──

    x_fi_purchase_history_ids = fields.Many2many(
        'account.move.line',
        compute='_compute_purchase_history',
        string='Purchase History')
    x_fi_purchase_history_count = fields.Integer(
        compute='_compute_purchase_history',
        string='Bill Lines')

    # ────────────────────── Computed Methods ──────────────────────

    @api.depends('sync_mapping_ids', 'sync_mapping_ids.remote_qty_available',
                 'sync_mapping_ids.remote_qty_forecast')
    def _compute_remote_stock(self):
        for product in self:
            mappings = product.sync_mapping_ids
            product.remote_qty_available = sum(mappings.mapped('remote_qty_available'))
            product.remote_qty_forecast = sum(mappings.mapped('remote_qty_forecast'))
            product.has_remote_mapping = bool(mappings)

    @api.depends('list_price', 'standard_price')
    def _compute_profit(self):
        for rec in self:
            rec.x_fi_profit_amount = rec.list_price - rec.standard_price

    @api.depends('list_price', 'categ_id',
                 'x_fi_brand_ids', 'x_fi_brand_ids.primary_discount_pct',
                 'x_fi_brand_ids.secondary_discount_pct',
                 'x_fi_brand_ids.pricing_rule_ids',
                 'x_fi_brand_ids.pricing_rule_ids.pricing_method',
                 'x_fi_brand_ids.pricing_rule_ids.primary_discount_pct',
                 'x_fi_brand_ids.pricing_rule_ids.secondary_discount_pct',
                 'x_fi_brand_ids.pricing_rule_ids.flat_discount_pct',
                 'x_fi_brand_ids.pricing_rule_ids.fixed_rebate_amount',
                 'x_fi_brand_ids.pricing_rule_ids.fixed_cost_price')
    def _compute_expected_cost(self):
        for rec in self:
            brand = rec.x_fi_brand_ids[:1]
            if brand and rec.list_price > 0:
                rec.x_fi_expected_cost = brand.calculate_cost_from_msrp(
                    rec.list_price, product_tmpl=rec)
            else:
                rec.x_fi_expected_cost = 0.0

    def _compute_purchase_history(self):
        AML = self.env['account.move.line']
        for rec in self:
            variant_ids = rec.product_variant_ids.ids
            if variant_ids:
                lines = AML.search([
                    ('product_id', 'in', variant_ids),
                    ('move_id.move_type', '=', 'in_invoice'),
                    ('move_id.state', '=', 'posted'),
                    ('price_unit', '>', 0),
                ], order='date desc', limit=500)
                rec.x_fi_purchase_history_ids = lines
                rec.x_fi_purchase_history_count = len(lines)
            else:
                rec.x_fi_purchase_history_ids = AML
                rec.x_fi_purchase_history_count = 0

    # ────────────────────── Margin Compute / Inverse / Onchange ──────────────────────

    @api.depends('list_price', 'standard_price')
    def _compute_margin_pct(self):
        for rec in self:
            if rec.list_price > 0 and rec.standard_price > 0:
                rec.x_fi_margin_pct = round(
                    ((rec.list_price - rec.standard_price) / rec.list_price) * 100, 2)
            else:
                rec.x_fi_margin_pct = 0.0

    def _inverse_margin_pct(self):
        for rec in self:
            if rec.standard_price > 0 and 0 < rec.x_fi_margin_pct < 100:
                rec.list_price = round(
                    rec.standard_price / (1 - rec.x_fi_margin_pct / 100), 2)

    @api.onchange('list_price', 'standard_price')
    def _onchange_price_to_margin(self):
        """Real-time margin recalculation when user changes price or cost."""
        for rec in self:
            if rec.list_price > 0 and rec.standard_price > 0:
                new_margin = round(
                    ((rec.list_price - rec.standard_price) / rec.list_price) * 100, 2)
                if abs(rec.x_fi_margin_pct - new_margin) > 0.01:
                    rec.x_fi_margin_pct = new_margin
            elif rec.list_price <= 0:
                rec.x_fi_margin_pct = 0.0

    @api.onchange('x_fi_margin_pct')
    def _onchange_margin_to_price(self):
        """Real-time sale price recalculation when user changes margin."""
        for rec in self:
            if rec.standard_price > 0 and 0 < rec.x_fi_margin_pct < 100:
                new_price = round(
                    rec.standard_price / (1 - rec.x_fi_margin_pct / 100), 2)
                if abs(rec.list_price - new_price) > 0.01:
                    rec.list_price = new_price

    # ────────────────────── Variant Margin Propagation ──────────────────────

    def _propagate_margin_to_variants(self):
        """Apply the template margin to variant price offsets (skip overrides)."""
        for rec in self:
            margin = rec.x_fi_margin_pct or 0.0
            if margin <= 0 or margin >= 100:
                continue
            variants = rec.product_variant_ids.filtered(
                lambda v: not v.x_fi_margin_override
                          and v.standard_price > 0)
            for var in variants:
                var._apply_margin_to_variant(margin)

    def _propagate_shipping_to_variants(self, shipping_cost):
        """Copy the template's shipping cost to all variants."""
        for rec in self:
            for var in rec.product_variant_ids:
                if abs((var.x_fi_shipping_cost or 0.0) - shipping_cost) > 0.001:
                    var.x_fi_shipping_cost = shipping_cost

    def action_apply_margin_to_all_variants(self):
        """Button: apply template margin to every non-overridden variant."""
        self.ensure_one()
        margin = self.x_fi_margin_pct or 0.0
        if margin <= 0 or margin >= 100:
            return
        variants = self.product_variant_ids.filtered(
            lambda v: not v.x_fi_margin_override
                      and v.standard_price > 0)
        for var in variants:
            var._apply_margin_to_variant(margin)
        skipped = len(self.product_variant_ids.filtered(
            lambda v: v.x_fi_margin_override))
        msg = f'{len(variants)} variant prices updated to {margin}% margin.'
        if skipped:
            msg += f' {skipped} overridden variant(s) skipped.'
        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': 'Margin Applied',
                'message': msg,
                'type': 'success',
                'sticky': False,
            },
        }

    # ────────────────────── Case Conversion ──────────────────────

    @staticmethod
    def _apply_case_conversion(name, mode):
        if not name or not mode or mode == 'none':
            return name
        if mode == 'upper':
            return name.upper()
        if mode == 'lower':
            return name.lower()
        if mode == 'sentence':
            return name[0].upper() + name[1:].lower() if len(name) > 1 else name.upper()
        if mode == 'capitalized':
            return name.title()
        return name

    def _get_effective_case_mode(self, vals=None):
        global_mode = self.env['ir.config_parameter'].sudo().get_param(
            'fusion_inventory.case_conversion', 'none')
        if global_mode and global_mode != 'none':
            return global_mode
        if vals and vals.get('x_fi_case_conversion'):
            return vals['x_fi_case_conversion']
        if self and self.x_fi_case_conversion:
            return self.x_fi_case_conversion
        return 'none'

    @api.onchange('x_fi_case_conversion')
    def _onchange_case_conversion(self):
        for rec in self:
            mode = rec._get_effective_case_mode()
            if mode != 'none' and rec.name:
                rec.name = self._apply_case_conversion(rec.name, mode)

    @api.model_create_multi
    def create(self, vals_list):
        global_mode = self.env['ir.config_parameter'].sudo().get_param(
            'fusion_inventory.case_conversion', 'none')
        for vals in vals_list:
            name = vals.get('name', '')
            if name:
                mode = global_mode if (global_mode and global_mode != 'none') else vals.get('x_fi_case_conversion', 'none')
                if mode and mode != 'none':
                    vals['name'] = self._apply_case_conversion(name, mode)
                if global_mode and global_mode != 'none':
                    vals.setdefault('x_fi_case_conversion', global_mode)

        return super().create(vals_list)

    def write(self, vals):
        res = super().write(vals)
        if ('name' in vals or 'x_fi_case_conversion' in vals) and not self.env.context.get('_fi_converting_case'):
            for rec in self:
                mode = rec._get_effective_case_mode(vals)
                if mode != 'none':
                    converted = self._apply_case_conversion(rec.name, mode)
                    if converted and converted != rec.name:
                        rec.with_context(_fi_converting_case=True).write({'name': converted})
        if ('x_fi_margin_pct' in vals or 'list_price' in vals) and not self.env.context.get('_fi_propagating'):
            self.with_context(_fi_propagating=True)._propagate_margin_to_variants()
        if 'x_fi_shipping_cost' in vals:
            self._propagate_shipping_to_variants(vals['x_fi_shipping_cost'])
        return res

    # ────────────────────── Purchase History / Cost Sync ──────────────────────

    def action_view_purchase_history(self):
        self.ensure_one()
        variant_ids = self.product_variant_ids.ids
        return {
            'type': 'ir.actions.act_window',
            'name': f'Purchase History: {self.name}',
            'res_model': 'account.move.line',
            'view_mode': 'list',
            'domain': [
                ('product_id', 'in', variant_ids),
                ('move_id.move_type', '=', 'in_invoice'),
                ('move_id.state', '=', 'posted'),
                ('price_unit', '>', 0),
            ],
            'context': {'create': False},
            'limit': 50,
        }

    def action_refresh_cost_from_bills(self):
        """Pull the latest non-zero vendor bill price into product cost."""
        self._sync_cost_from_latest_bill()
        return {
            'type': 'ir.actions.client',
            'tag': 'display_notification',
            'params': {
                'title': 'Cost Refreshed',
                'message': 'Product cost updated from the latest vendor bill.',
                'type': 'success',
                'sticky': False,
            },
        }

    def _sync_cost_from_latest_bill(self):
        """For each variant, find the most recent posted vendor bill line
        with a non-zero price and set its standard_price."""
        AML = self.env['account.move.line']
        for rec in self:
            for variant in rec.product_variant_ids:
                latest = AML.search([
                    ('product_id', '=', variant.id),
                    ('move_id.move_type', '=', 'in_invoice'),
                    ('move_id.state', '=', 'posted'),
                    ('price_unit', '>', 0),
                ], order='date desc, id desc', limit=1)
                if latest and abs(variant.standard_price - latest.price_unit) > 0.001:
                    old = variant.standard_price
                    variant.standard_price = latest.price_unit
                    _logger.info(
                        'Cost synced for %s: %.2f -> %.2f (bill %s)',
                        variant.display_name, old, latest.price_unit,
                        latest.move_id.name)

    @api.model
    def _cron_sync_all_costs_from_bills(self):
        """Batch job: update every product's cost from latest vendor bill."""
        auto_update = self.env['ir.config_parameter'].sudo().get_param(
            'fusion_inventory.auto_update_cost', 'True')
        if auto_update != 'True':
            return
        products = self.search([])
        products._sync_cost_from_latest_bill()
        _logger.info('Batch cost sync complete for %d products.', len(products))
