from odoo import models, fields, api, _
from odoo.exceptions import UserError
import logging
import requests
import base64
import time

_logger = logging.getLogger(__name__)

class ShopifySKUSimulationLine(models.Model):
    _name = 'shopify.sku.simulation.line'
    _description = 'Línea de Simulación de SKU de Shopify'

    config_id = fields.Many2one('shopify.config', string='Configuración', ondelete='cascade')
    product_name = fields.Char(string='Producto', readonly=True)
    variant_name = fields.Char(string='Variante', readonly=True)
    shopify_product_id = fields.Char(string='ID Producto Shopify', readonly=True)
    shopify_variant_id = fields.Char(string='ID Variante Shopify', readonly=True)
    shopify_handle = fields.Char(string='Handle (Shopify)', readonly=True)
    shopify_title = fields.Char(string='Título (Shopify)', readonly=True)
    old_sku = fields.Char(string='SKU Actual', readonly=True)
    suggested_sku = fields.Char(string='SKU Sugerido (Editable)')
    barcode = fields.Char(string='Código de Barras (Shopify)', readonly=True)
    shopify_inventory_item_id = fields.Char(string='ID Inventario Shopify', readonly=True)

    # EAN-13 generado por nuestra herramienta
    generated_barcode = fields.Char(string='EAN-13 Generado', readonly=True)
    barcode_sync_state = fields.Selection([
        ('none',    'Sin generar'),
        ('pending', 'Listo para subir'),
        ('done',    'Subido ✓'),
        ('error',   'Error'),
    ], string='Estado Barcode', default='none', readonly=True)
    barcode_sync_error = fields.Char(string='Error Barcode', readonly=True)

    # New fields for better filtering
    product_type = fields.Char(string='Tipo de Producto', readonly=True)
    product_image = fields.Char(string='URL Imagen', readonly=True)
    sku_category_code = fields.Char(string='Código Cat.', readonly=True)
    image_html = fields.Html(string='Imagen (HTML)', compute='_compute_image_html')
    
    is_selected = fields.Boolean(string='Sincronizar', default=False)
    sku_source = fields.Selection([
        ('csv', 'CSV'),
        ('logic', 'Lógica')
    ], string='Origen', default='logic')
    sync_state = fields.Selection([
        ('draft', 'Sin SKU'),
        ('has_sku', 'Tiene SKU'),
        ('synced', 'Sincronizado'),
        ('error', 'Error')
    ], string='Estado', default='draft', readonly=True)
    
    is_imported = fields.Boolean(string='Importado a Odoo', default=False, readonly=True)
    
    # Nuevo: Campo para saber si ya lo trajimos a Odoo
    exists_in_odoo = fields.Boolean(string='En Odoo', compute='_compute_exists_in_odoo', store=False)
    sync_history = fields.Text(string='Historial de Sync', readonly=True)
    last_sync_date = fields.Datetime(string='Último Ajuste Realizado', readonly=True)
    shopify_qty_total = fields.Float(string='Total Shopify', default=0.0)
    odoo_qty_total = fields.Float(string='Total Odoo', compute='_compute_odoo_qty_total', store=False)
    shopify_stock_info = fields.Html(string='Detalle Stock Shopify', readonly=True)
    odoo_stock_info = fields.Html(string='Stock en Odoo', compute='_compute_odoo_stock_info')
    
    # Campo para detectar duplicados EN SHOPIFY (previos a la corrección)
    is_shopify_duplicate = fields.Boolean(string='Duplicado en Web', compute='_compute_shopify_duplicate', store=False)
    duplicate_with = fields.Char(string='Conflicto con...', compute='_compute_shopify_duplicate', store=False)

    @api.depends('old_sku', 'config_id', 'config_id.simulation_line_ids.old_sku')
    def _compute_shopify_duplicate(self):
        """Detecta duplicados agrupando en memoria para evitar queries con NewId."""
        # Agrupar por config para procesar en bloque (eficiente)
        by_config = {}
        for record in self:
            config_id = record.config_id.id or 0
            by_config.setdefault(config_id, self.env['shopify.sku.simulation.line'])
            by_config[config_id] |= record

        for config_id, records in by_config.items():
            # Obtener todas las líneas del config usando el set de registros ya en memoria
            if records and records[0].config_id:
                all_lines = records[0].config_id.simulation_line_ids
            else:
                all_lines = records

            # Construir mapa SKU -> [líneas]
            sku_map = {}
            for line in all_lines:
                if line.old_sku:
                    sku_map.setdefault(line.old_sku, [])
                    sku_map[line.old_sku].append(line)

            # Marcar los que están en el conjunto actual
            for record in records:
                if not record.old_sku:
                    record.is_shopify_duplicate = False
                    record.duplicate_with = ''
                    continue

                siblings = [l for l in sku_map.get(record.old_sku, []) if l.id != record.id]
                record.is_shopify_duplicate = bool(siblings)
                if siblings:
                    names = [f"{s.product_name} ({s.variant_name})" for s in siblings[:3]]
                    note = ', '.join(names)
                    if len(siblings) > 3:
                        note += f'... y otros {len(siblings) - 3}'
                    record.duplicate_with = note
                else:
                    record.duplicate_with = ''

    @api.depends('old_sku', 'config_id.location_mapping_ids')
    def _compute_odoo_stock_info(self):
        for record in self:
            if not record.old_sku:
                record.odoo_stock_info = "Sin SKU"
                continue
            
            product = self.env['product.product'].search([('default_code', '=', record.old_sku)], limit=1)
            if not product:
                record.odoo_stock_info = "No existe en Odoo"
                continue
            
            html = """<table class="table table-sm" style="width:100%; font-size: 13px;">
                        <thead><tr style="background-color: #f8f9fa;"><th>Ubicación</th><th class="text-right">Stock</th></tr></thead>
                        <tbody>"""
            mappings = record.config_id.location_mapping_ids
            has_data = False
            for m in mappings:
                if m.odoo_location_id:
                    quants = self.env['stock.quant'].search([
                        ('product_id', '=', product.id),
                        ('location_id', '=', m.odoo_location_id.id)
                    ])
                    qty = sum(quants.mapped('quantity'))
                    color = "#28a745" if qty > 0 else "#6c757d"
                    html += f'<tr><td>{m.odoo_location_id.display_name}</td><td class="text-right"><span class="badge badge-pill" style="background-color: {color}; color: white; padding: 5px 10px;">{qty}</span></td></tr>'
                    has_data = True
            
            if not has_data:
                html += f'<tr><td colspan="2" class="text-muted text-center">Total Odoo: {product.qty_available}</td></tr>'
            
            html += "</tbody></table>"
            record.odoo_stock_info = html

    @api.depends('old_sku')
    def _compute_exists_in_odoo(self):
        for record in self:
            # BUSQUEDA: Solo por SKU real (old_sku)
            found = False
            if record.old_sku:
                found = self.env['product.product'].search([('default_code', '=', record.old_sku)], limit=1)
            
            record.exists_in_odoo = bool(found)

    @api.depends('old_sku')
    def _compute_odoo_qty_total(self):
        for record in self:
            if not record.old_sku:
                record.odoo_qty_total = 0.0
                continue
            product = self.env['product.product'].search([('default_code', '=', record.old_sku)], limit=1)
            record.odoo_qty_total = product.qty_available if product else 0.0

    def action_import_to_odoo(self):
        """Paso 1 (Importación): Crea el ítem en Odoo y le pone el stock inicial de Shopify."""
        self.ensure_one()
        if self.exists_in_odoo:
            raise UserError("Este producto ya existe en Odoo con el SKU %s" % self.old_sku)

        config = self.config_id
        if not config.shopify_location_id:
            raise UserError("Debes vincular una sucursal de Shopify primero (Botón 'Traer Ubicaciones').")

        # 1. Buscar Almacén y Ubicación principal de Odoo
        warehouse = self.env['stock.warehouse'].search([], limit=1)
        shop_domain = config.shop_url.strip().lower()
        if '://' in shop_domain:
            shop_domain = shop_domain.split('://')[1].split('/')[0]
        else:
            shop_domain = shop_domain.split('/')[0]
            
        token = config._ensure_access_token()
        headers = {"X-Shopify-Access-Token": token}
        
        # 1. Obtener la variante de Shopify para tener el inventory_item_id real
        url_v = f"https://{shop_domain}/admin/api/2024-01/variants/{self.shopify_variant_id}.json"
        resp_v = requests.get(url_v, headers=headers, timeout=15)
        
        if resp_v.status_code != 200:
            raise Exception("Error consultando Shopify: %s" % resp_v.text)
        
        variant_data = resp_v.json().get('variant', {})
        inv_item_id = variant_data.get('inventory_item_id')
        
        # 2. Obtener Stock de Shopify Multi-Sucursal
        mappings = config.location_mapping_ids
        total_qty = 0
        
        # 3. Crear o Actualizar el Producto en Odoo
        try:
            shopify_price = float(variant_data.get('price', 0.0))
        except:
            shopify_price = 0.0
        
        full_name = self.product_name
        if self.variant_name and self.variant_name.lower() not in ('default title', 'default', ''):
            full_name = f"{self.product_name} ({self.variant_name})"
        
        # BUSQUEDA HÍBRIDA PARA ACTUALIZACIÓN:
        # 1. Buscamos por el SKU Correcto (M)
        odoo_variant = self.env['product.product'].search([('default_code', '=', self.old_sku)], limit=1)
        
        # 2. Si no existe, buscamos por el SKU Viejo (U) para CORREGIRLO
        if not odoo_variant and self.suggested_sku:
            odoo_variant = self.env['product.product'].search([('default_code', '=', self.suggested_sku)], limit=1)
            if odoo_variant:
                _logger.info("Corrigiendo SKU en Odoo: %s -> %s", odoo_variant.default_code, self.old_sku)

        if odoo_variant:
            # Si existe (incluso si tenía el SKU viejo), lo actualizamos al SKU CORRECTO
            odoo_variant.write({
                'active': True,
                'name': full_name,
                'default_code': self.old_sku, # <--- AQUÍ SE CORRIGE EL SKU
                'list_price': shopify_price,
                'barcode': self.barcode or self.generated_barcode,
                'shopify_variant_id': self.shopify_variant_id,
                'shopify_inventory_item_id': str(inv_item_id),
                'sale_ok': True,
                'purchase_ok': True,
            })
            odoo_variant.product_tmpl_id.write({
                'name': full_name,
                'active': True,
                'sale_ok': True,
                'purchase_ok': True,
            })
        else:
            # Si no existe de ninguna forma, creamos uno nuevo con el SKU CORRECTO
            template = self.env['product.template'].create({
                'name': full_name,
                'type': 'product',
                'list_price': shopify_price,
                'barcode': self.barcode or self.generated_barcode,
                'default_code': self.old_sku, # <--- SKU CORRECTO DE SHOPIFY
                'categ_id': self.env.ref('product.product_category_all').id,
                'sale_ok': True,
                'purchase_ok': True,
                'active': True,
                'company_id': False, # GLOBAL
            })
            odoo_variant = template.product_variant_id
            odoo_variant.write({
                'shopify_variant_id': self.shopify_variant_id,
                'shopify_inventory_item_id': str(inv_item_id),
            })

        # 4. Sincronización de Inventario (MULTI-SUCURSAL OPTIMIZADA)
        if mappings:
            # Traemos todos los niveles de una vez para ser precisos
            url_levels = f"https://{shop_domain}/admin/api/2024-01/inventory_levels.json?inventory_item_ids={inv_item_id}"
            resp_levels = requests.get(url_levels, headers=headers, timeout=15)
            
            stock_info = []
            if resp_levels.status_code == 200:
                levels = resp_levels.json().get('inventory_levels', [])
                # Crear mapa de Shopify Location ID -> Qty
                shopify_stock_map = {str(l['location_id']): l.get('available', 0) for l in levels}
                
                for mapping in mappings:
                    if not mapping.shopify_location_id or not mapping.odoo_location_id:
                        continue
                    
                    loc_id_str = str(mapping.shopify_location_id)
                    qty = shopify_stock_map.get(loc_id_str, 0)
                    
                    # Registrar para el campo informativo
                    stock_info.append(f"{mapping.shopify_location_name or loc_id_str}: {qty}")
                    
                    if qty > 0:
                        self.env['stock.quant'].with_context(inventory_mode=True).create({
                            'product_id': odoo_variant.id,
                            'location_id': mapping.odoo_location_id.id,
                            'inventory_quantity': qty,
                        }).action_apply_inventory()
                        total_qty += qty
                
                self.shopify_stock_info = " | ".join(stock_info) if stock_info else "Sin stock en sucursales mapeadas"

        # 5. Descarga de Imagen
        if self.product_image:
            try:
                img_resp = requests.get(self.product_image, timeout=10)
                if img_resp.status_code == 200:
                    odoo_variant.product_tmpl_id.write({
                        'image_1920': base64.b64encode(img_resp.content)
                    })
            except:
                pass

        self.write({
            'is_imported': True,
            'shopify_inventory_item_id': str(inv_item_id)
        })

    @api.depends('product_image')
    def _compute_image_html(self):
        for record in self:
            if record.product_image:
                record.image_html = f'<img src="{record.product_image}" style="max-height: 50px; max-width: 50px; border-radius: 4px;"/>'
            else:
                record.image_html = False

    def action_refresh_shopify_stock_info(self):
        """Consulta el stock en Shopify y actualiza solo el campo informativo, sin tocar Odoo."""
        self.ensure_one()
        config = self.config_id
        shop_domain = config.shop_url
        token = config._ensure_access_token()
        headers = {'X-Shopify-Access-Token': token}
        
        # Asegurar ID de inventario
        inv_item_id = self.shopify_inventory_item_id
        if not inv_item_id or inv_item_id == '0':
            url_v = f"https://{shop_domain}/admin/api/2024-01/variants/{self.shopify_variant_id}.json"
            resp_v = requests.get(url_v, headers=headers, timeout=10)
            if resp_v.status_code == 200:
                inv_item_id = str(resp_v.json().get('variant', {}).get('inventory_item_id', '0'))
                self.shopify_inventory_item_id = inv_item_id
        
        if not inv_item_id or inv_item_id == '0':
            raise UserError("No se pudo obtener el ID de inventario de Shopify.")
            
        url_levels = f"https://{shop_domain}/admin/api/2024-01/inventory_levels.json?inventory_item_ids={inv_item_id}"
        resp_levels = requests.get(url_levels, headers=headers, timeout=15)
        
        if resp_levels.status_code == 200:
            levels = resp_levels.json().get('inventory_levels', [])
            shopify_stock_map = {str(l['location_id']): l.get('available', 0) for l in levels}
            
            html = """<table class="table table-sm" style="width:100%; font-size: 13px;">
                        <thead><tr style="background-color: #e9ecef;"><th>Sucursal Shopify</th><th class="text-right">Stock</th></tr></thead>
                        <tbody>"""
            mappings = config.location_mapping_ids
            for mapping in mappings:
                if not mapping.shopify_location_id: continue
                qty = shopify_stock_map.get(str(mapping.shopify_location_id), 0)
                color = "#17a2b8" if qty > 0 else "#6c757d"
                html += f'<tr><td>{mapping.shopify_location_name}</td><td class="text-right"><span class="badge badge-pill" style="background-color: {color}; color: white; padding: 5px 10px;">{qty}</span></td></tr>'
            
            html += "</tbody></table>"
            self.shopify_stock_info = html
            self.shopify_qty_total = sum(shopify_stock_map.values())
            self.sync_history = f"✅ CONSULTA EXITOSA [{fields.Datetime.now()}] - Se obtuvo el stock real de Shopify."
        else:
            self.sync_history = f"❌ ERROR DE CONSULTA [{fields.Datetime.now()}] - {resp_levels.text}"
            raise UserError(f"Error: {resp_levels.text}")

    def action_sync_inventory_only(self):
        """Actualiza el inventario en Odoo basándose en lo que hay en Shopify actualmente."""
        self.ensure_one()
        if not self.exists_in_odoo:
            raise UserError("Este producto no está en Odoo. Usa el botón 'Importar' primero.")
            
        config = self.config_id
        shop_domain = config.shop_url
        token = config._ensure_access_token()
        headers = {'X-Shopify-Access-Token': token}
        
        # 1. Asegurar que tenemos el inventory_item_id
        inv_item_id = self.shopify_inventory_item_id
        if not inv_item_id or inv_item_id == '0':
            url_v = f"https://{shop_domain}/admin/api/2024-01/variants/{self.shopify_variant_id}.json"
            resp_v = requests.get(url_v, headers=headers, timeout=10)
            if resp_v.status_code == 200:
                inv_item_id = str(resp_v.json().get('variant', {}).get('inventory_item_id', '0'))
                self.shopify_inventory_item_id = inv_item_id
        
        if not inv_item_id or inv_item_id == '0':
            raise UserError("No se pudo obtener el ID de inventario de Shopify.")
            
        # 2. Buscar el producto en Odoo
        product = self.env['product.product'].search([('default_code', '=', self.old_sku)], limit=1)
        if not product:
            raise UserError(f"No se encontró el producto con SKU {self.old_sku} en Odoo.")
            
        # 3. Consultar Shopify (Multi-sucursal)
        url_levels = f"https://{shop_domain}/admin/api/2024-01/inventory_levels.json?inventory_item_ids={inv_item_id}"
        resp_levels = requests.get(url_levels, headers=headers, timeout=15)
        
        if resp_levels.status_code == 200:
            levels = resp_levels.json().get('inventory_levels', [])
            shopify_stock_map = {str(l['location_id']): l.get('available', 0) for l in levels}
            
            html = """<table class="table table-sm" style="width:100%; font-size: 13px;">
                        <thead><tr style="background-color: #e9ecef;"><th>Sucursal Shopify</th><th class="text-right">Stock</th></tr></thead>
                        <tbody>"""
            mappings = config.location_mapping_ids
            for mapping in mappings:
                if not mapping.shopify_location_id or not mapping.odoo_location_id:
                    continue
                
                qty = shopify_stock_map.get(str(mapping.shopify_location_id), 0)
                # Actualizar Odoo: Ponemos la cantidad exacta (Inventory Adjustment)
                self.env['stock.quant'].with_context(inventory_mode=True).create({
                    'product_id': product.id,
                    'location_id': mapping.odoo_location_id.id,
                    'inventory_quantity': qty,
                }).action_apply_inventory()
                
                color = "#17a2b8" if qty > 0 else "#6c757d"
                html += f'<tr><td>{mapping.shopify_location_name}</td><td class="text-right"><span class="badge badge-pill" style="background-color: {color}; color: white; padding: 5px 10px;">{qty}</span></td></tr>'
            
            html += "</tbody></table>"
            self.shopify_stock_info = html
            self.shopify_qty_total = sum(shopify_stock_map.values())
            self.last_sync_date = fields.Datetime.now()
            self.sync_history = f"🚀 ÉXITO [{fields.Datetime.now()}] - Inventario sincronizado correctamente en todos los almacenes mapeados."
            
            return {
                'effect': {
                    'fadeout': 'slow',
                    'message': 'Inventario sincronizado y actualizado ✓',
                    'type': 'rainbow_man',
                }
            }
        else:
            error_msg = f"❌ ERROR DE SYNC [{fields.Datetime.now()}] - {resp_levels.text}"
            self.sync_history = error_msg
            raise UserError(f"Error consultando Shopify: {resp_levels.text}")

    def _get_shopify_total_qty(self):
        """Helper para obtener el stock total de Shopify considerando mapeos."""
        self.ensure_one()
        config = self.config_id
        shop_domain = config.shop_url
        headers = {'X-Shopify-Access-Token': config.access_token}
        
        inv_item_id = self.shopify_inventory_item_id
        
        # SI NO TENEMOS EL ID, LO BUSCAMOS EN SHOPIFY AL VUELO
        if not inv_item_id or inv_item_id == '0':
            url_v = f"https://{shop_domain}/admin/api/2024-01/variants/{self.shopify_variant_id}.json"
            resp_v = requests.get(url_v, headers=headers, timeout=10)
            if resp_v.status_code == 200:
                variant_data = resp_v.json().get('variant', {})
                inv_item_id = str(variant_data.get('inventory_item_id', '0'))
                if inv_item_id != '0':
                    self.write({'shopify_inventory_item_id': inv_item_id})
            
        if not inv_item_id or inv_item_id == '0':
            return 0
            
        total = 0
        mappings = config.location_mapping_ids
        if not mappings:
            return 0
            
        # Creamos una lista de los IDs de Shopify que nos interesan
        interested_location_ids = [str(m.shopify_location_id) for m in mappings if m.shopify_location_id]
        
        try:
            # Consultamos TODOS los niveles del item de una sola vez
            url = f"https://{shop_domain}/admin/api/2024-01/inventory_levels.json?inventory_item_ids={inv_item_id}"
            resp = requests.get(url, headers=headers, timeout=12)
            
            # MANEJO DE RATE LIMIT: Si vamos muy rápido, esperamos y reintentamos una vez
            if resp.status_code == 429:
                _logger.warning("Rate limit alcanzado en auditoría masiva. Esperando 3 segundos...")
                time.sleep(3)
                resp = requests.get(url, headers=headers, timeout=12)

            if resp.status_code == 200:
                levels = resp.json().get('inventory_levels', [])
                found_any = False
                for level in levels:
                    loc_id = str(level.get('location_id'))
                    if loc_id in interested_location_ids:
                        total += level.get('available', 0)
                        found_any = True
                
                # Si no encontramos ninguna ubicación de las interesadas, pero la respuesta fue exitosa,
                # devolvemos 0 (aquí sí es un 0 real).
                return total
            else:
                _logger.error("Shopify API Error %s en SKU %s: %s", resp.status_code, self.old_sku, resp.text)
                # IMPORTANTE: No retornamos 0 si hubo un error de API, retornamos None o levantamos error
                # para que el hilo de auditoría sepa que NO fue un 0 real.
                return -999999 
        except Exception as e:
            _logger.error("Excepción consultando stock en SKU %s: %s", self.old_sku, str(e))
            return -999999
