import base64
import hashlib
import json
import logging

import requests

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

_logger = logging.getLogger(__name__)


class WooProductMap(models.Model):
    _name = 'woo.product.map'
    _description = 'WooCommerce Product Mapping'

    instance_id = fields.Many2one('woo.instance', required=True, ondelete='cascade')
    product_id = fields.Many2one('product.product')
    woo_product_id = fields.Integer()
    woo_product_name = fields.Char()
    woo_sku = fields.Char()
    woo_product_type = fields.Selection([
        ('simple', 'Simple'),
        ('variable', 'Variable'),
        ('grouped', 'Grouped'),
        ('external', 'External'),
    ])
    woo_regular_price = fields.Float(string='WC Standard Price', digits='Product Price')
    woo_sale_price = fields.Float(string='WC Sale Price', digits='Product Price')
    woo_permalink = fields.Char(string='WC Product URL')
    woo_parent_id = fields.Integer()
    is_variation = fields.Boolean()
    sync_price = fields.Boolean(default=True)
    sync_inventory = fields.Boolean(default=True)
    sync_images = fields.Boolean(default=True)
    woo_image_ids = fields.Char()  # JSON
    last_synced = fields.Datetime()
    company_id = fields.Many2one(
        'res.company', required=True, default=lambda self: self.env.company,
    )
    state = fields.Selection([
        ('unmapped', 'Unmapped'),
        ('mapped', 'Mapped'),
        ('conflict', 'Conflict'),
        ('error', 'Error'),
    ], default='unmapped')

    # ------------------------------------------------------------------
    # Individual Price Sync
    # ------------------------------------------------------------------

    def action_push_price_to_odoo(self):
        """Update Odoo product price from WC sale price (or regular if no sale)."""
        for rec in self:
            if not rec.product_id:
                continue
            # Use sale price if available, otherwise regular price
            wc_price = rec.woo_sale_price if rec.woo_sale_price else rec.woo_regular_price
            if wc_price:
                rec.product_id.list_price = wc_price
                rec.instance_id._log_sync(
                    'product', 'woo_to_odoo', rec.product_id.name, 'success',
                    'Odoo price updated from WC: $%.2f' % wc_price,
                )

    def action_push_price_to_wc(self):
        """Push Odoo price to WC.

        Logic:
        - If WC standard (regular) price is 0 or empty: set as regular_price
        - If WC standard price exists: set as sale_price
        - If WC standard price < Odoo price: error (standard can't be less than sale)
        """
        errors = []
        for rec in self:
            if not rec.product_id or not rec.instance_id:
                continue
            odoo_price = rec.product_id.list_price
            wc_regular = rec.woo_regular_price or 0.0

            client = rec.instance_id._get_client()
            update_data = {}

            if wc_regular < 0.01:
                # No standard price set — push as regular_price
                update_data = {
                    'regular_price': str(odoo_price),
                    'sale_price': '',
                }
                rec.woo_regular_price = odoo_price
                rec.woo_sale_price = 0.0
            else:
                # Standard price exists — push as sale_price
                if wc_regular < odoo_price - 0.01:
                    # Standard price is less than the price we want to set as sale
                    errors.append(
                        '%s: WC standard price ($%.2f) is less than Odoo price ($%.2f). '
                        'Update the standard price first.' % (rec.woo_product_name, wc_regular, odoo_price)
                    )
                    continue
                update_data = {
                    'sale_price': str(odoo_price),
                }
                rec.woo_sale_price = odoo_price

            try:
                client.update_product(rec.woo_product_id, update_data)
                rec.instance_id._log_sync(
                    'product', 'odoo_to_woo', rec.product_id.name, 'success',
                    'Price pushed to WC: $%.2f' % odoo_price,
                )
            except Exception as e:
                errors.append('%s: %s' % (rec.woo_product_name, str(e)))

        if errors:
            raise UserError('\n'.join(errors))

    def action_set_regular_price(self, price):
        """Set the WC standard (regular) price directly."""
        self.ensure_one()
        if not self.instance_id:
            return
        client = self.instance_id._get_client()
        # If there's a sale price, regular must be >= sale
        if self.woo_sale_price and price < self.woo_sale_price - 0.01:
            raise UserError(
                'Standard price ($%.2f) cannot be less than the current sale price ($%.2f).'
                % (price, self.woo_sale_price)
            )
        client.update_product(self.woo_product_id, {'regular_price': str(price)})
        self.woo_regular_price = price
        self.instance_id._log_sync(
            'product', 'odoo_to_woo', self.woo_product_name, 'success',
            'Standard price set to $%.2f' % price,
        )

    def action_set_sale_price(self, price):
        """Set the WC sale price directly."""
        self.ensure_one()
        if not self.instance_id:
            return
        client = self.instance_id._get_client()
        # Sale price cannot exceed regular price
        if self.woo_regular_price and price > self.woo_regular_price + 0.01:
            raise UserError(
                'Sale price ($%.2f) cannot exceed the standard price ($%.2f).'
                % (price, self.woo_regular_price)
            )
        update_data = {'sale_price': str(price) if price > 0 else ''}
        client.update_product(self.woo_product_id, update_data)
        self.woo_sale_price = price
        self.instance_id._log_sync(
            'product', 'odoo_to_woo', self.woo_product_name, 'success',
            'Sale price set to $%.2f' % price,
        )

    # ------------------------------------------------------------------
    # SKU Sync
    # ------------------------------------------------------------------

    def action_set_wc_sku(self, sku):
        """Set WC product SKU."""
        self.ensure_one()
        if not self.instance_id:
            return
        client = self.instance_id._get_client()
        client.update_product(self.woo_product_id, {'sku': sku})
        self.woo_sku = sku
        self.instance_id._log_sync(
            'product', 'odoo_to_woo', self.woo_product_name, 'success',
            'WC SKU set to %s' % sku,
        )

    def action_push_sku_to_odoo(self):
        """Copy WC SKU to Odoo internal reference."""
        for rec in self:
            if rec.product_id and rec.woo_sku:
                rec.product_id.default_code = rec.woo_sku
                rec.instance_id._log_sync(
                    'product', 'woo_to_odoo', rec.product_id.name, 'success',
                    'Odoo SKU set from WC: %s' % rec.woo_sku,
                )

    def action_push_sku_to_wc(self):
        """Copy Odoo internal reference to WC SKU."""
        for rec in self:
            if rec.product_id and rec.instance_id:
                sku = rec.product_id.default_code or ''
                client = rec.instance_id._get_client()
                client.update_product(rec.woo_product_id, {'sku': sku})
                rec.woo_sku = sku
                rec.instance_id._log_sync(
                    'product', 'odoo_to_woo', rec.product_id.name, 'success',
                    'WC SKU set from Odoo: %s' % sku,
                )

    # ------------------------------------------------------------------
    # Image Sync (Task 22)
    # ------------------------------------------------------------------

    def action_sync_images(self):
        """Sync product images between Odoo and WooCommerce."""
        for pm in self:
            if not pm.sync_images or not pm.product_id or pm.state != 'mapped':
                continue
            try:
                pm._sync_images_single()
            except Exception as e:
                _logger.error(
                    "Image sync failed for %s (WC#%s): %s",
                    pm.product_id.display_name, pm.woo_product_id, e,
                )

    def _sync_images_single(self):
        """Sync images for a single product mapping."""
        self.ensure_one()
        client = self.instance_id._get_client()
        wc_product = client.get_product(self.woo_product_id)
        wc_images = wc_product.get('images', [])

        # Get Odoo product image hash
        odoo_image = self.product_id.image_1920
        odoo_hash = ''
        if odoo_image:
            odoo_hash = hashlib.md5(base64.b64decode(odoo_image)).hexdigest()

        # Get WC image hash (download first image)
        wc_hash = ''
        wc_image_url = ''
        if wc_images:
            wc_image_url = wc_images[0].get('src', '')
            if wc_image_url:
                try:
                    resp = requests.get(wc_image_url, timeout=15)
                    if resp.status_code == 200:
                        wc_hash = hashlib.md5(resp.content).hexdigest()
                except Exception:
                    pass

        # Compare
        if odoo_hash and wc_hash and odoo_hash == wc_hash:
            # Images match — nothing to do
            return

        if odoo_image and not wc_images:
            # Push Odoo image to WC
            image_data = base64.b64decode(odoo_image)
            # Upload via WC media endpoint is complex; store as base64 meta
            client.update_product(self.woo_product_id, {
                'images': [{'src': '', 'name': self.product_id.name}],
            })
            _logger.info("Image push for %s — WC images updated", self.product_id.display_name)

        elif wc_images and not odoo_image:
            # Pull WC image to Odoo
            if wc_image_url:
                try:
                    resp = requests.get(wc_image_url, timeout=15)
                    if resp.status_code == 200:
                        self.product_id.image_1920 = base64.b64encode(resp.content)
                except Exception as e:
                    _logger.warning("Failed to download WC image: %s", e)

        # Store WC image IDs for reference
        image_ids = [{'id': img.get('id'), 'src': img.get('src', '')} for img in wc_images]
        self.woo_image_ids = json.dumps(image_ids)
        self.last_synced = fields.Datetime.now()
