import base64
import hashlib
import json
import logging
import secrets

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

from ..lib.woo_api_client import WooApiClient

_logger = logging.getLogger(__name__)


class WooInstance(models.Model):
    _name = 'woo.instance'
    _description = 'WooCommerce Instance'
    _inherit = ['mail.thread']

    name = fields.Char(required=True, tracking=True)
    url = fields.Char(required=True, tracking=True)
    consumer_key = fields.Char(groups='base.group_system')
    consumer_secret = fields.Char(groups='base.group_system')
    webhook_secret = fields.Char(groups='base.group_system')
    wc_api_version = fields.Char(default='wc/v3')
    odoo_api_key = fields.Char(groups='base.group_system')
    company_id = fields.Many2one(
        'res.company', required=True, default=lambda self: self.env.company,
    )
    sync_interval = fields.Selection(
        [('5', '5 Min'), ('15', '15 Min'), ('30', '30 Min'), ('60', '1 Hour')],
        default='15',
    )
    sync_products = fields.Boolean(default=True)
    sync_orders = fields.Boolean(default=True)
    sync_invoices = fields.Boolean(default=True)
    sync_inventory = fields.Boolean(default=True)
    sync_customers = fields.Boolean(default=True)
    default_warehouse_id = fields.Many2one('stock.warehouse')
    notify_on_failure = fields.Boolean()
    notify_user_ids = fields.Many2many('res.users')
    state = fields.Selection(
        [('draft', 'Draft'), ('connected', 'Connected'), ('error', 'Error')],
        default='draft', tracking=True,
    )
    last_sync = fields.Datetime(readonly=True)

    # Relational
    product_map_ids = fields.One2many('woo.product.map', 'instance_id')
    order_ids = fields.One2many('woo.order', 'instance_id')
    customer_ids = fields.One2many('woo.customer', 'instance_id')
    sync_log_ids = fields.One2many('woo.sync.log', 'instance_id')

    # Computed
    mapped_count = fields.Integer(compute='_compute_counts')
    unmapped_count = fields.Integer(compute='_compute_counts')
    error_count = fields.Integer(compute='_compute_counts')

    @api.depends('product_map_ids.state', 'sync_log_ids')
    def _compute_counts(self):
        for rec in self:
            rec.mapped_count = self.env['woo.product.map'].search_count([
                ('instance_id', '=', rec.id), ('state', '=', 'mapped'),
            ])
            rec.unmapped_count = self.env['woo.product.map'].search_count([
                ('instance_id', '=', rec.id), ('state', '=', 'unmapped'),
            ])
            yesterday = fields.Datetime.subtract(fields.Datetime.now(), hours=24)
            rec.error_count = self.env['woo.sync.log'].search_count([
                ('instance_id', '=', rec.id),
                ('state', '=', 'failed'),
                ('create_date', '>=', yesterday),
            ])

    def _get_client(self):
        """Return a WooApiClient instance for this WooCommerce connection."""
        self.ensure_one()
        if not self.consumer_key or not self.consumer_secret:
            raise UserError("Consumer key and secret are required.")
        return WooApiClient(
            url=self.url,
            consumer_key=self.consumer_key,
            consumer_secret=self.consumer_secret,
            api_version=self.wc_api_version or 'wc/v3',
        )

    def action_test_connection(self):
        """Test the WooCommerce connection and update state."""
        self.ensure_one()
        try:
            client = self._get_client()
            success, info = client.test_connection()
            if success:
                self.state = 'connected'
                self.message_post(body=f"Connection successful. WooCommerce version: {info}")
            else:
                self.state = 'error'
                self.message_post(body=f"Connection failed: {info}")
        except Exception as exc:
            self.state = 'error'
            self.message_post(body=f"Connection error: {exc}")
            _logger.exception("WooCommerce connection test failed for %s", self.name)

    def action_generate_api_key(self):
        """Generate a random API key for the Odoo webhook endpoint."""
        self.ensure_one()
        self.odoo_api_key = secrets.token_urlsafe(32)

    def action_view_product_maps(self):
        """Open product mapping list filtered by this instance."""
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'name': 'Product Mapping',
            'res_model': 'woo.product.map',
            'view_mode': 'list,form',
            'domain': [('instance_id', '=', self.id)],
            'context': {'default_instance_id': self.id},
        }

    def action_view_sync_errors(self):
        """Open sync log filtered to errors for this instance."""
        self.ensure_one()
        twenty_four_ago = fields.Datetime.subtract(fields.Datetime.now(), hours=24)
        return {
            'type': 'ir.actions.act_window',
            'name': 'Sync Errors (24h)',
            'res_model': 'woo.sync.log',
            'view_mode': 'list,form',
            'domain': [
                ('instance_id', '=', self.id),
                ('state', '=', 'failed'),
                ('create_date', '>=', twenty_four_ago),
            ],
        }

    def _log_sync(self, sync_type, direction, record_ref, state, message=''):
        """Create a woo.sync.log record for this instance."""
        self.ensure_one()
        self.env['woo.sync.log'].sudo().create({
            'instance_id': self.id,
            'sync_type': sync_type,
            'direction': direction,
            'record_ref': record_ref,
            'state': state,
            'message': message,
            'company_id': self.company_id.id,
        })

    # ------------------------------------------------------------------
    # UI actions (called from OWL product mapping + dashboard)
    # ------------------------------------------------------------------

    def action_fetch_products(self):
        """Fetch products from WooCommerce and run auto-match."""
        self.ensure_one()
        if self.state != 'connected':
            raise UserError("Please test the connection first.")
        client = self._get_client()
        ProductMap = self.env['woo.product.map']
        page = 1
        total_fetched = 0
        auto_matched = 0

        while True:
            try:
                products = client.get_products(page=page, per_page=100)
            except Exception as e:
                self._log_sync('product', 'woo_to_odoo', self.name, 'failed', str(e))
                raise UserError("Failed to fetch products: %s" % str(e))
            if not products:
                break

            for wc_prod in products:
                wc_id = wc_prod.get('id')
                # Skip if already mapped
                existing = ProductMap.search([
                    ('instance_id', '=', self.id),
                    ('woo_product_id', '=', wc_id),
                ], limit=1)
                if existing:
                    continue

                wc_sku = wc_prod.get('sku', '') or ''
                wc_name = wc_prod.get('name', '')
                wc_type = wc_prod.get('type', 'simple')

                # Try SKU match
                odoo_product = False
                match_state = 'unmapped'
                if wc_sku:
                    odoo_product = self.env['product.product'].search([
                        ('default_code', '=', wc_sku),
                    ], limit=1)
                    if odoo_product:
                        match_state = 'mapped'
                        auto_matched += 1

                ProductMap.create({
                    'instance_id': self.id,
                    'product_id': odoo_product.id if odoo_product else False,
                    'woo_product_id': wc_id,
                    'woo_product_name': wc_name,
                    'woo_sku': wc_sku,
                    'woo_product_type': wc_type if wc_type in ('simple', 'variable', 'grouped', 'external') else 'simple',
                    'state': match_state,
                    'company_id': self.company_id.id,
                })
                total_fetched += 1

                # Fetch variations for variable products
                if wc_type == 'variable':
                    try:
                        var_page = 1
                        while True:
                            variations = client.get_product_variations(wc_id, page=var_page, per_page=100)
                            if not variations:
                                break
                            for var in variations:
                                var_id = var.get('id')
                                var_existing = ProductMap.search([
                                    ('instance_id', '=', self.id),
                                    ('woo_product_id', '=', var_id),
                                ], limit=1)
                                if var_existing:
                                    continue

                                var_sku = var.get('sku', '') or ''
                                var_name = wc_name + ' - ' + ', '.join(
                                    [a.get('option', '') for a in var.get('attributes', [])]
                                )
                                var_product = False
                                var_state = 'unmapped'
                                if var_sku:
                                    var_product = self.env['product.product'].search([
                                        ('default_code', '=', var_sku),
                                    ], limit=1)
                                    if var_product:
                                        var_state = 'mapped'
                                        auto_matched += 1

                                ProductMap.create({
                                    'instance_id': self.id,
                                    'product_id': var_product.id if var_product else False,
                                    'woo_product_id': var_id,
                                    'woo_product_name': var_name,
                                    'woo_sku': var_sku,
                                    'woo_product_type': 'simple',
                                    'woo_parent_id': wc_id,
                                    'is_variation': True,
                                    'state': var_state,
                                    'company_id': self.company_id.id,
                                })
                                total_fetched += 1
                            var_page += 1
                            if len(variations) < 100:
                                break
                    except Exception:
                        _logger.warning("Failed to fetch variations for product %s", wc_id)

            page += 1
            if len(products) < 100:
                break

        self._log_sync('product', 'woo_to_odoo', self.name, 'success',
                       'Fetched %d products, auto-matched %d by SKU' % (total_fetched, auto_matched))
        return True

    def action_sync(self):
        """Manual sync trigger from the UI."""
        self.ensure_one()
        if self.state != 'connected':
            raise UserError("Instance is not connected.")
        try:
            if self.sync_products:
                self._sync_products()
            if self.sync_orders:
                self._sync_orders()
            if self.sync_inventory:
                self._sync_inventory()
            if self.sync_customers:
                self._sync_customers()
            self.last_sync = fields.Datetime.now()
        except Exception as e:
            _logger.error("Manual sync failed for %s: %s", self.name, str(e))
            self._log_sync('product', 'odoo_to_woo', self.name, 'failed', str(e))
            raise UserError("Sync failed: %s" % str(e))
        return True

    # ------------------------------------------------------------------
    # Cron entry points
    # ------------------------------------------------------------------

    @api.model
    def _cron_sync_products(self):
        """Sync product prices and inventory for all connected instances."""
        instances = self.search([('state', '=', 'connected'), ('sync_products', '=', True)])
        for instance in instances:
            try:
                instance._sync_products()
            except Exception as e:
                _logger.error("Product sync failed for %s: %s", instance.name, str(e))
                instance._log_sync('product', 'woo_to_odoo', instance.name, 'failed', str(e))
                instance._notify_failure('product', str(e))

    @api.model
    def _cron_sync_orders(self):
        """Fetch new orders from all connected WooCommerce instances."""
        instances = self.search([('state', '=', 'connected'), ('sync_orders', '=', True)])
        for instance in instances:
            try:
                instance._sync_orders()
            except Exception as e:
                _logger.error("Order sync failed for %s: %s", instance.name, str(e))
                instance._log_sync('order', 'woo_to_odoo', instance.name, 'failed', str(e))
                instance._notify_failure('order', str(e))

    @api.model
    def _cron_sync_inventory(self):
        """Push inventory levels to WooCommerce for all connected instances."""
        instances = self.search([('state', '=', 'connected'), ('sync_inventory', '=', True)])
        for instance in instances:
            try:
                instance._sync_inventory()
            except Exception as e:
                _logger.error("Inventory sync failed for %s: %s", instance.name, str(e))
                instance._log_sync('inventory', 'odoo_to_woo', instance.name, 'failed', str(e))
                instance._notify_failure('inventory', str(e))

    @api.model
    def _cron_sync_customers(self):
        """Sync customer address updates to WooCommerce."""
        instances = self.search([('state', '=', 'connected'), ('sync_customers', '=', True)])
        for instance in instances:
            try:
                instance._sync_customers()
            except Exception as e:
                _logger.error("Customer sync failed for %s: %s", instance.name, str(e))
                instance._log_sync('customer', 'odoo_to_woo', instance.name, 'failed', str(e))
                instance._notify_failure('customer', str(e))

    @api.model
    def _cron_health_check(self):
        """Ping all connected instances and flag unreachable ones."""
        instances = self.search([('state', '!=', 'draft')])
        for instance in instances:
            try:
                client = instance._get_client()
                success, _info = client.test_connection()
                if success:
                    if instance.state == 'error':
                        instance.state = 'connected'
                        instance.message_post(body="Health check passed — connection restored.")
                else:
                    instance.state = 'error'
                    instance.message_post(body="Health check failed — store unreachable.")
                    instance._notify_failure('health', 'Store unreachable during health check.')
            except Exception as e:
                instance.state = 'error'
                instance.message_post(body=f"Health check error: {e}")
                instance._notify_failure('health', str(e))
                _logger.error("Health check failed for %s: %s", instance.name, e)

    # ------------------------------------------------------------------
    # Order Sync (Task 20)
    # ------------------------------------------------------------------

    def _sync_orders(self):
        """Fetch new WC orders and create Odoo SOs."""
        self.ensure_one()
        client = self._get_client()
        page = 1
        while True:
            orders = client.get_orders(page=page, status='processing,on-hold,pending')
            if not orders:
                break
            for wc_order in orders:
                try:
                    self._sync_order_from_wc(wc_order)
                except Exception as e:
                    _logger.error(
                        "Failed to sync WC order %s for %s: %s",
                        wc_order.get('id'), self.name, e,
                    )
                    self._log_sync(
                        'order', 'woo_to_odoo',
                        f"WC#{wc_order.get('id')}", 'failed', str(e),
                    )
            page += 1
            if len(orders) < 100:
                break
        self.last_sync = fields.Datetime.now()

    def _sync_order_from_wc(self, wc_order):
        """Process a single WC order into an Odoo sale order."""
        self.ensure_one()
        woo_order_id = wc_order['id']

        # Dedup check
        existing = self.env['woo.order'].search([
            ('instance_id', '=', self.id),
            ('woo_order_id', '=', woo_order_id),
        ], limit=1)
        if existing:
            return existing

        # Find/create customer
        partner = self._find_or_create_customer(wc_order)

        # Create sale order
        so_vals = self._prepare_sale_order_vals(wc_order, partner)
        sale_order = self.env['sale.order'].create(so_vals)

        # Add order lines
        for line_item in wc_order.get('line_items', []):
            line_vals = self._prepare_order_line_vals(sale_order, line_item)
            if line_vals:
                self.env['sale.order.line'].create(line_vals)

        # Add shipping lines
        for shipping in wc_order.get('shipping_lines', []):
            shipping_vals = self._prepare_shipping_line_vals(sale_order, shipping)
            if shipping_vals:
                self.env['sale.order.line'].create(shipping_vals)

        # Add fee lines
        for fee in wc_order.get('fee_lines', []):
            fee_vals = {
                'order_id': sale_order.id,
                'name': fee.get('name', 'Fee'),
                'product_uom_qty': 1,
                'price_unit': float(fee.get('total', 0)),
            }
            self.env['sale.order.line'].create(fee_vals)

        # Confirm SO
        sale_order.action_confirm()

        # Create draft invoice
        invoice = False
        if self.sync_invoices:
            try:
                invoice = sale_order._create_invoices()
            except Exception as e:
                _logger.warning("Could not auto-create invoice for %s: %s", sale_order.name, e)

        # Create woo.order tracking record
        woo_order = self.env['woo.order'].create({
            'instance_id': self.id,
            'sale_order_id': sale_order.id,
            'woo_order_id': woo_order_id,
            'woo_order_number': wc_order.get('number', str(woo_order_id)),
            'woo_status': wc_order.get('status', ''),
            'invoice_id': invoice.id if invoice else False,
            'state': 'confirmed',
            'company_id': self.company_id.id,
        })

        # Link invoice to woo.order
        if invoice:
            invoice.woo_order_id = woo_order.id

        # Push SO ref back to WC
        try:
            client = self._get_client()
            client.update_order(woo_order_id, {
                'meta_data': [
                    {'key': '_odoo_order_id', 'value': str(sale_order.id)},
                    {'key': '_odoo_order_ref', 'value': sale_order.name},
                ]
            })
        except Exception as e:
            _logger.warning("Failed to push SO ref to WC: %s", e)

        self._log_sync(
            'order', 'woo_to_odoo', sale_order.name, 'success',
            f'Created from WC order #{woo_order.woo_order_number}',
        )
        return woo_order

    def _find_or_create_customer(self, wc_order):
        """Find or create an Odoo partner from a WC order's billing data."""
        self.ensure_one()
        billing = wc_order.get('billing', {})
        email = billing.get('email', '').strip().lower()
        wc_customer_id = wc_order.get('customer_id', 0)

        if not email:
            # Guest checkout — create a one-off partner
            name = f"{billing.get('first_name', '')} {billing.get('last_name', '')}".strip()
            return self.env['res.partner'].create({
                'name': name or 'WooCommerce Guest',
                'email': '',
                'company_id': self.company_id.id,
            })

        # Check existing woo.customer link first
        if wc_customer_id:
            woo_cust = self.env['woo.customer'].search([
                ('instance_id', '=', self.id),
                ('woo_customer_id', '=', wc_customer_id),
            ], limit=1)
            if woo_cust:
                return woo_cust.partner_id

        # Search by email
        partner = self.env['res.partner'].search([
            ('email', '=ilike', email),
            '|', ('company_id', '=', self.company_id.id), ('company_id', '=', False),
        ], limit=1)

        if not partner:
            partner = self.env['res.partner'].create(
                self._prepare_partner_vals(billing)
            )

        # Ensure woo.customer link exists
        if wc_customer_id and not self.env['woo.customer'].search([
            ('instance_id', '=', self.id),
            ('woo_customer_id', '=', wc_customer_id),
        ], limit=1):
            self.env['woo.customer'].create({
                'instance_id': self.id,
                'partner_id': partner.id,
                'woo_customer_id': wc_customer_id,
                'woo_email': email,
                'last_synced': fields.Datetime.now(),
                'company_id': self.company_id.id,
            })

        return partner

    def _prepare_partner_vals(self, billing):
        """Build res.partner vals from WC billing data."""
        self.ensure_one()
        name = f"{billing.get('first_name', '')} {billing.get('last_name', '')}".strip()
        country = self.env['res.country'].search([
            ('code', '=', billing.get('country', '')),
        ], limit=1)
        state = False
        if billing.get('state') and country:
            state = self.env['res.country.state'].search([
                ('country_id', '=', country.id),
                ('code', '=', billing.get('state')),
            ], limit=1)
        return {
            'name': name or billing.get('email', 'WooCommerce Customer'),
            'email': billing.get('email', ''),
            'phone': billing.get('phone', ''),
            'street': billing.get('address_1', ''),
            'street2': billing.get('address_2', ''),
            'city': billing.get('city', ''),
            'zip': billing.get('postcode', ''),
            'country_id': country.id if country else False,
            'state_id': state.id if state else False,
            'company_id': self.company_id.id,
        }

    def _prepare_sale_order_vals(self, wc_order, partner):
        """Build sale.order vals dict from a WC order."""
        self.ensure_one()
        currency = self.env['res.currency'].search([
            ('name', '=', wc_order.get('currency', 'CAD')),
        ], limit=1)

        vals = {
            'partner_id': partner.id,
            'company_id': self.company_id.id,
            'client_order_ref': f"WC#{wc_order.get('number', wc_order['id'])}",
        }
        if currency:
            # Set pricelist with matching currency if available
            pricelist = self.env['product.pricelist'].search([
                ('currency_id', '=', currency.id),
                '|', ('company_id', '=', self.company_id.id), ('company_id', '=', False),
            ], limit=1)
            if pricelist:
                vals['pricelist_id'] = pricelist.id

        if self.default_warehouse_id:
            vals['warehouse_id'] = self.default_warehouse_id.id

        # Shipping address
        shipping = wc_order.get('shipping', {})
        if shipping.get('first_name') or shipping.get('last_name'):
            ship_name = f"{shipping.get('first_name', '')} {shipping.get('last_name', '')}".strip()
            ship_country = self.env['res.country'].search([
                ('code', '=', shipping.get('country', '')),
            ], limit=1)
            ship_state = False
            if shipping.get('state') and ship_country:
                ship_state = self.env['res.country.state'].search([
                    ('country_id', '=', ship_country.id),
                    ('code', '=', shipping.get('state')),
                ], limit=1)
            ship_partner = self.env['res.partner'].search([
                ('parent_id', '=', partner.id),
                ('type', '=', 'delivery'),
                ('street', '=', shipping.get('address_1', '')),
                ('city', '=', shipping.get('city', '')),
            ], limit=1)
            if not ship_partner:
                ship_partner = self.env['res.partner'].create({
                    'parent_id': partner.id,
                    'type': 'delivery',
                    'name': ship_name,
                    'street': shipping.get('address_1', ''),
                    'street2': shipping.get('address_2', ''),
                    'city': shipping.get('city', ''),
                    'zip': shipping.get('postcode', ''),
                    'country_id': ship_country.id if ship_country else False,
                    'state_id': ship_state.id if ship_state else False,
                    'company_id': self.company_id.id,
                })
            vals['partner_shipping_id'] = ship_partner.id

        return vals

    def _prepare_order_line_vals(self, sale_order, line_item):
        """Build sale.order.line vals from a WC line_item dict."""
        self.ensure_one()
        wc_product_id = line_item.get('product_id', 0)
        wc_variation_id = line_item.get('variation_id', 0)

        # Look up mapped product
        lookup_id = wc_variation_id or wc_product_id
        product_map = self.env['woo.product.map'].search([
            ('instance_id', '=', self.id),
            ('woo_product_id', '=', lookup_id),
            ('state', '=', 'mapped'),
        ], limit=1)

        product = product_map.product_id if product_map else False

        # If not mapped, try SKU match
        if not product:
            sku = line_item.get('sku', '')
            if sku:
                product = self.env['product.product'].search([
                    ('default_code', '=', sku),
                    '|', ('company_id', '=', self.company_id.id), ('company_id', '=', False),
                ], limit=1)

        vals = {
            'order_id': sale_order.id,
            'name': line_item.get('name', 'WooCommerce Product'),
            'product_uom_qty': line_item.get('quantity', 1),
            'price_unit': float(line_item.get('price', 0)),
        }

        if product:
            vals['product_id'] = product.id

        # Tax mapping
        taxes = []
        for tax_entry in line_item.get('taxes', []):
            # WC sends tax class on the line item
            pass
        wc_tax_class = line_item.get('tax_class', '')
        if wc_tax_class:
            odoo_tax = self.env['woo.tax.map'].get_odoo_tax(self, wc_tax_class)
            if odoo_tax:
                taxes.append(odoo_tax.id)
        elif line_item.get('total_tax') and float(line_item.get('total_tax', 0)) > 0:
            # Standard tax class
            odoo_tax = self.env['woo.tax.map'].get_odoo_tax(self, 'standard')
            if odoo_tax:
                taxes.append(odoo_tax.id)

        if taxes:
            vals['tax_id'] = [(6, 0, taxes)]

        return vals

    def _prepare_shipping_line_vals(self, sale_order, shipping_line):
        """Build sale.order.line vals from a WC shipping_lines entry."""
        self.ensure_one()
        total = float(shipping_line.get('total', 0))
        if not total:
            return False
        return {
            'order_id': sale_order.id,
            'name': shipping_line.get('method_title', 'Shipping'),
            'product_uom_qty': 1,
            'price_unit': total,
        }

    # ------------------------------------------------------------------
    # Product / Price Sync (Task 22)
    # ------------------------------------------------------------------

    def _sync_products(self):
        """Sync product prices between Odoo and WooCommerce."""
        self.ensure_one()
        client = self._get_client()
        maps = self.env['woo.product.map'].search([
            ('instance_id', '=', self.id),
            ('state', '=', 'mapped'),
            ('sync_price', '=', True),
            ('product_id', '!=', False),
        ])

        for pm in maps:
            try:
                wc_product = client.get_product(pm.woo_product_id)
                wc_price = float(wc_product.get('regular_price') or 0)
                odoo_price = pm.product_id.list_price

                if abs(wc_price - odoo_price) < 0.01:
                    # Prices match — nothing to do
                    pm.last_synced = fields.Datetime.now()
                    continue

                # Check if Odoo price was changed since last sync
                odoo_changed = True
                woo_changed = True

                if pm.last_synced:
                    # If product write_date is newer than last sync, Odoo changed
                    odoo_changed = pm.product_id.write_date > pm.last_synced
                    # WC always returns current price, assume changed if different
                    woo_changed = True

                if odoo_changed and woo_changed:
                    # Both changed — conflict
                    self.env['woo.conflict'].create({
                        'instance_id': self.id,
                        'conflict_type': 'product',
                        'map_id': pm.id,
                        'field_name': 'price',
                        'odoo_value': str(odoo_price),
                        'woo_value': str(wc_price),
                        'company_id': self.company_id.id,
                    })
                    pm.state = 'conflict'
                    self._log_sync(
                        'product', 'woo_to_odoo', pm.product_id.display_name,
                        'conflict', f'Price conflict: Odoo=${odoo_price}, WC=${wc_price}',
                    )
                elif odoo_changed:
                    # Odoo is authoritative — push to WC
                    client.update_product(pm.woo_product_id, {
                        'regular_price': str(odoo_price),
                    })
                    self._log_sync(
                        'product', 'odoo_to_woo', pm.product_id.display_name,
                        'success', f'Price updated to ${odoo_price}',
                    )
                else:
                    # WC changed — pull into Odoo
                    pm.product_id.list_price = wc_price
                    self._log_sync(
                        'product', 'woo_to_odoo', pm.product_id.display_name,
                        'success', f'Price updated to ${wc_price}',
                    )

                pm.last_synced = fields.Datetime.now()
            except Exception as e:
                _logger.error(
                    "Product price sync failed for %s (WC#%s): %s",
                    pm.product_id.display_name, pm.woo_product_id, e,
                )
                pm.state = 'error'
                self._log_sync(
                    'product', 'odoo_to_woo',
                    pm.product_id.display_name, 'failed', str(e),
                )

    def _sync_product_from_wc(self, wc_data):
        """Handle an inbound WC product webhook — update price if mapped."""
        self.ensure_one()
        wc_product_id = wc_data.get('id')
        if not wc_product_id:
            return

        pm = self.env['woo.product.map'].search([
            ('instance_id', '=', self.id),
            ('woo_product_id', '=', wc_product_id),
            ('state', '=', 'mapped'),
        ], limit=1)
        if not pm or not pm.product_id:
            return

        if pm.sync_price:
            wc_price = float(wc_data.get('regular_price') or 0)
            if wc_price and abs(wc_price - pm.product_id.list_price) > 0.01:
                pm.product_id.list_price = wc_price
                self._log_sync(
                    'product', 'woo_to_odoo', pm.product_id.display_name,
                    'success', f'Price updated via webhook to ${wc_price}',
                )

        pm.last_synced = fields.Datetime.now()

    # ------------------------------------------------------------------
    # Inventory Sync (Task 22)
    # ------------------------------------------------------------------

    def _sync_inventory(self):
        """Push Odoo stock levels to WooCommerce."""
        self.ensure_one()
        client = self._get_client()
        maps = self.env['woo.product.map'].search([
            ('instance_id', '=', self.id),
            ('state', '=', 'mapped'),
            ('sync_inventory', '=', True),
            ('product_id', '!=', False),
        ])

        for pm in maps:
            try:
                product = pm.product_id
                if self.default_warehouse_id:
                    # Get qty for specific warehouse
                    quant = self.env['stock.quant'].search([
                        ('product_id', '=', product.id),
                        ('location_id', 'child_of',
                         self.default_warehouse_id.lot_stock_id.id),
                    ])
                    qty = sum(quant.mapped('quantity')) - sum(quant.mapped('reserved_quantity'))
                else:
                    qty = product.qty_available

                qty = max(int(qty), 0)

                client.update_product(pm.woo_product_id, {
                    'stock_quantity': qty,
                    'manage_stock': True,
                })
                pm.last_synced = fields.Datetime.now()
                self._log_sync(
                    'inventory', 'odoo_to_woo', product.display_name,
                    'success', f'Stock set to {qty}',
                )
            except Exception as e:
                _logger.error(
                    "Inventory sync failed for %s (WC#%s): %s",
                    pm.product_id.display_name, pm.woo_product_id, e,
                )
                self._log_sync(
                    'inventory', 'odoo_to_woo',
                    pm.product_id.display_name, 'failed', str(e),
                )

    # ------------------------------------------------------------------
    # Customer Sync (Task 25)
    # ------------------------------------------------------------------

    def _sync_customers(self):
        """Push updated Odoo partner addresses to WooCommerce."""
        self.ensure_one()
        client = self._get_client()
        customers = self.env['woo.customer'].search([
            ('instance_id', '=', self.id),
            ('woo_customer_id', '>', 0),
        ])

        for cust in customers:
            try:
                partner = cust.partner_id
                # Only sync if partner was modified since last sync
                if cust.last_synced and partner.write_date <= cust.last_synced:
                    continue

                billing_data = {
                    'first_name': (partner.name or '').split(' ', 1)[0],
                    'last_name': (partner.name or '').split(' ', 1)[1] if ' ' in (partner.name or '') else '',
                    'email': partner.email or '',
                    'phone': partner.phone or '',
                    'address_1': partner.street or '',
                    'address_2': partner.street2 or '',
                    'city': partner.city or '',
                    'postcode': partner.zip or '',
                    'country': partner.country_id.code if partner.country_id else '',
                    'state': partner.state_id.code if partner.state_id else '',
                }

                client.update_customer(cust.woo_customer_id, {
                    'billing': billing_data,
                    'shipping': billing_data,
                })

                cust.last_synced = fields.Datetime.now()
                self._log_sync(
                    'customer', 'odoo_to_woo', partner.display_name,
                    'success', 'Address updated in WooCommerce',
                )
            except Exception as e:
                _logger.error(
                    "Customer sync failed for %s (WC#%s): %s",
                    cust.partner_id.display_name, cust.woo_customer_id, e,
                )
                self._log_sync(
                    'customer', 'odoo_to_woo',
                    cust.partner_id.display_name, 'failed', str(e),
                )

    def _sync_customer_from_wc(self, wc_data):
        """Handle an inbound WC customer webhook — update partner if linked."""
        self.ensure_one()
        wc_customer_id = wc_data.get('id')
        email = wc_data.get('email', '').strip().lower()
        if not wc_customer_id:
            return

        cust = self.env['woo.customer'].search([
            ('instance_id', '=', self.id),
            ('woo_customer_id', '=', wc_customer_id),
        ], limit=1)

        billing = wc_data.get('billing', {})
        if cust:
            partner = cust.partner_id
            # Update partner address from WC
            vals = self._prepare_partner_vals(billing)
            vals.pop('company_id', None)
            partner.write(vals)
            cust.last_synced = fields.Datetime.now()
            self._log_sync(
                'customer', 'woo_to_odoo', partner.display_name,
                'success', 'Updated from WC webhook',
            )
        elif email:
            # New customer from WC
            self.env['woo.customer']._find_or_create(self, email, wc_data)

    # ------------------------------------------------------------------
    # Notification
    # ------------------------------------------------------------------

    def _notify_failure(self, sync_type, error_message):
        """Send email notification on sync failure."""
        self.ensure_one()
        if not self.notify_on_failure or not self.notify_user_ids:
            return
        template = self.env.ref(
            'fusion_woocommerce.woo_sync_failure_notification', raise_if_not_found=False,
        )
        if template:
            for user in self.notify_user_ids:
                template.with_context(
                    sync_type=sync_type,
                    error_message=error_message,
                ).send_mail(self.id, force_send=True, email_values={'email_to': user.email})
