# stock.py
import os
import json
import time
import csv
import requests
from datetime import datetime
from collections import defaultdict


class ChangeTracker:
    """Compara extrações sucessivas e conserva alterações visíveis por 2 horas."""
    DURACAO_SEGUNDOS = 2 * 60 * 60

    def __init__(self, directory):
        self.path = os.path.join(directory, "alteracoes_recentes.json")

    @staticmethod
    def _normalizar(value):
        """Serialização estável, incluindo listas/dicionários aninhados do stock."""
        return json.dumps(value, ensure_ascii=False, sort_keys=True, default=str)

    @staticmethod
    def _texto(value):
        if value is None:
            return "(vazio)"
        if isinstance(value, (dict, list)):
            return "dados atualizados"
        return str(value)

    def _carregar(self):
        try:
            with open(self.path, "r", encoding="utf-8") as file:
                return json.load(file)
        except (OSError, json.JSONDecodeError):
            return {"snapshots": {}, "events": {}}

    def _guardar(self, state):
        with open(self.path, "w", encoding="utf-8") as file:
            json.dump(state, file, ensure_ascii=False, indent=2)

    def inicializar_de_ficheiro(self, dataset, json_path):
        """Usa uma exportação já existente como referência na primeira execução."""
        state = self._carregar()
        if dataset in state.setdefault("snapshots", {}):
            return
        try:
            with open(json_path, "r", encoding="utf-8") as file:
                records = json.load(file)
            if isinstance(records, list):
                state["snapshots"][dataset] = {
                    str(item.get("id")): item for item in records if item.get("id")
                }
                self._guardar(state)
                print(f"🕘 Histórico de alterações iniciado com {len(records)} registos de {dataset}.")
        except (OSError, json.JSONDecodeError):
            pass

    def analisar(self, dataset, records, key="id"):
        """Anota cópias dos registos com _alteracoes enquanto estas não expirarem."""
        now = datetime.now().timestamp()
        state = self._carregar()
        snapshots = state.setdefault("snapshots", {})
        events = state.setdefault("events", {})
        current = {str(item.get(key)): item for item in records if item.get(key)}
        previous = snapshots.get(dataset)

        # A primeira leitura é só a base de comparação.
        if previous is None:
            snapshots[dataset] = current
            self._guardar(state)
            return records

        for record_id, item in current.items():
            old = previous.get(record_id)
            changed = []
            if old is None:
                changed = ["Novo registo"]
            else:
                fields = sorted(set(old) | set(item))
                for field in fields:
                    if field.startswith("_"):
                        continue
                    if self._normalizar(old.get(field)) != self._normalizar(item.get(field)):
                        changed.append(f"{field}: {self._texto(old.get(field))} → {self._texto(item.get(field))}")
            if changed:
                events.setdefault(dataset, {})[record_id] = {"at": now, "fields": changed}

        snapshots[dataset] = current
        active = {}
        for record_id, event in events.get(dataset, {}).items():
            remaining = self.DURACAO_SEGUNDOS - (now - event.get("at", 0))
            if remaining > 0 and record_id in current:
                active[record_id] = {**event, "remaining": int(remaining)}
        events[dataset] = active
        self._guardar(state)

        annotated = []
        for item in records:
            event = active.get(str(item.get(key)))
            copy = dict(item)
            if event:
                copy["_alteracoes"] = event
            annotated.append(copy)
        return annotated

class StockAPI:
    def __init__(self, base_url="https://prod.mydimomaintmx.cloud"):
        self.base_url = base_url
        self.session = requests.Session()
        self.token = None
        self.token_file = os.path.join(os.path.expanduser("~/Desktop"), "dimo_token.json")
        
        # Flag para indicar que o token expirou (para o Dimo.py renovar)
        self.token_expired = False
        
        # Cache de stocks já consultados
        self.cache_file = os.path.join(os.path.expanduser("~/Desktop"), "stock_cache.json")
        self.cache = self._load_cache()
        
        # Contadores
        self.stats = {
            'total': 0,
            'com_stock': 0,
            'sem_stock': 0,
            'erros': 0,
            'cache_hits': 0,
        }
    
    def _load_cache(self):
        """Carrega cache de stocks consultados"""
        try:
            if os.path.exists(self.cache_file):
                with open(self.cache_file, 'r', encoding='utf-8') as f:
                    cache = json.load(f)
                    print(f"📦 Cache carregado: {len(cache)} peças já consultadas")
                    return cache
        except Exception as e:
            print(f"⚠️ Erro ao carregar cache: {e}")
        return {}
    
    def _save_cache(self):
        """Guarda cache de stocks consultados"""
        try:
            with open(self.cache_file, 'w', encoding='utf-8') as f:
                json.dump(self.cache, f, ensure_ascii=False)
        except Exception as e:
            print(f"⚠️ Erro ao guardar cache: {e}")
    
    def load_token(self):
        """Carrega o token do ficheiro (partilhado com Dimo.py)"""
        try:
            if os.path.exists(self.token_file):
                with open(self.token_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.token = data.get('token')
                    if self.token:
                        self.session.cookies.set('MX-Access-Token', self.token)
                        print(f"🔑 Token carregado: {self.token[:30]}...")
                        return True
            print("❌ Token não encontrado no ficheiro.")
            return False
        except Exception as e:
            print(f"⚠️ Erro ao carregar token: {e}")
            return False
    
    def recarregar_token(self):
        """
        Recarrega o token do ficheiro após ser renovado pelo Dimo.py
        Devolve True se o token for diferente do atual
        """
        old_token = self.token
        self.token_expired = False
        
        if self.load_token():
            if self.token != old_token:
                print(f"🔄 Token renovado: {self.token[:30]}...")
                return True
            else:
                print("⚠️ Token recarregado mas é igual ao anterior.")
                return False
        return False
    
    def _handle_401(self):
        """Trata erro 401 - marca token como expirado"""
        print("⚠️ Stock: Token expirado (401)!")
        self.token = None
        self.token_expired = True
    
    def get_all_parts(self, limit=5000):
        """
        Obtém a lista de todas as peças (sem stock detalhado)
        """
        if not self.token:
            print("❌ Erro: Token não definido!")
            return []
        
        all_items = []
        skip = 0
        take = min(100, limit)
        
        print(f"📥 A buscar lista de peças (limite: {limit})...")
        
        while len(all_items) < limit:
            print(f"📥 A buscar peças de {skip} a {skip + take}...")
            
            query = """
            query parts($skip: Int, $take: Int) {
              part(
                skip: $skip
                take: $take
                order: [{code: ASC}]
                filter: {}
              ) {
                items {
                  id
                  code
                  name
                  manufacturerName
                  manufacturerReference
                  partFamilyName
                  alert
                  sharedData
                }
                totalCount
              }
            }
            """
            
            headers = {
                "Authorization": f"Bearer {self.token}",
                "Content-Type": "application/json",
                "controller": "part",
                "action": "list",
                "tenant": "SERRATEC"
            }
            
            try:
                response = self.session.post(
                    f"{self.base_url}/graphql",
                    headers=headers,
                    json={
                        "query": query,
                        "variables": {"skip": skip, "take": take}
                    },
                    timeout=30
                )
                
                if response.status_code == 200:
                    result = response.json()
                    
                    if "errors" in result:
                        print(f"❌ Erro GraphQL: {result['errors']}")
                        break
                    
                    data = result.get("data", {}).get("part", {})
                    items = data.get("items", [])
                    total_count = data.get("totalCount", 0)
                    
                    all_items.extend(items)
                    print(f"✅ Obtidos {len(items)} registos (total: {len(all_items)}/{min(limit, total_count)})")
                    
                    if len(all_items) >= limit or len(all_items) >= total_count:
                        break
                    
                    skip += take
                    time.sleep(0.3)
                    
                elif response.status_code == 401:
                    self._handle_401()
                    return []
                else:
                    print(f"❌ Erro HTTP: {response.status_code}")
                    break
                    
            except Exception as e:
                print(f"❌ Erro: {e}")
                break
        
        print(f"📊 Total de peças: {len(all_items)}")
        return all_items
    
    def get_part_details(self, part_id):
        """
        Obtém os detalhes de uma peça (incluindo stock)
        
        GET /SERRATEC/api/Part/get?id=UUID
        
        Devolve None se token expirou (para o Dimo.py renovar)
        """
        if not self.token:
            return None
        
        # Verificar cache primeiro
        if part_id in self.cache:
            self.stats['cache_hits'] += 1
            return self.cache[part_id]
        
        url = f"{self.base_url}/SERRATEC/api/Part/get"
        params = {"id": part_id}
        
        headers = {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json",
            "tenant": "SERRATEC"
        }
        
        try:
            response = self.session.get(url, headers=headers, params=params, timeout=30)
            
            if response.status_code == 200:
                data = response.json()
                self.cache[part_id] = data
                return data
            elif response.status_code == 401:
                self._handle_401()
                return None
            else:
                print(f"⚠️ Erro HTTP {response.status_code} para peça {part_id}")
                return None
        except Exception as e:
            print(f"⚠️ Erro ao buscar peça {part_id}: {e}")
            return None
    
    def process_parts_with_stock(self, parts, limit=None, save_interval=50):
        """
        Processa cada peça, obtendo o stock detalhado
        
        Args:
            parts: lista de peças
            limit: número máximo de peças a processar (None = todas)
            save_interval: guardar cache a cada N peças
        
        Returns:
            Lista de peças enriquecidas com stock
            Interrompe se token_expired = True
        """
        if limit:
            parts = parts[:limit]
        
        total = len(parts)
        processadas = 0
        com_stock = 0
        erros = 0
        
        print(f"\n🔄 A processar {total} peças para obter stock detalhado...")
        print(f"⏱️ Estimativa: ~{total * 0.3 / 60:.1f} minutos")
        print("=" * 60)
        
        resultado = []
        
        for i, part in enumerate(parts, 1):
            part_id = part.get('id', '')
            code = part.get('code', '')
            
            if not part_id:
                continue
            
            # Se token expirou, parar imediatamente
            if self.token_expired:
                print(f"⚠️ Token expirado! A parar em {i}/{total} peças processadas.")
                break
            
            # Obter detalhes da peça (com cache)
            details = self.get_part_details(part_id)
            
            if details:
                # Extrair stock
                stocks = details.get('partStocks', [])
                
                # Consolidar stock
                stock_consolidado = self._consolidar_stock(stocks)
                
                # Adicionar stock à peça
                part_enriquecida = {
                    **part,
                    'stock': stock_consolidado,
                    'partCriticality': details.get('partCriticality', {}),
                    'standardPrice': details.get('standardPrice', {}).get('cost', 0),
                    'globalWaup': details.get('globalWaup', {}).get('cost', 0),
                }
                
                resultado.append(part_enriquecida)
                
                if stock_consolidado.get('total', 0) > 0:
                    com_stock += 1
                else:
                    self.stats['sem_stock'] += 1
            else:
                # Se token expirou, não conta como erro
                if self.token_expired:
                    break
                
                erros += 1
                # Adicionar mesmo sem stock
                resultado.append({
                    **part,
                    'stock': {'total': 0, 'disponivel': 0, 'reservado': 0, 'lojas': [], 'num_lojas': 0},
                    'partCriticality': {},
                    'standardPrice': 0,
                    'globalWaup': 0,
                })
            
            processadas += 1
            
            # Progresso a cada 50 peças
            if i % save_interval == 0 or i == total:
                print(f"📊 Progresso: {i}/{total} ({i*100//total}%) | ✅ Com stock: {com_stock} | ❌ Erros: {erros} | 💾 Cache: {self.stats['cache_hits']}")
                self._save_cache()
            
            # Rate limiting (apenas se não foi do cache)
            if part_id not in self.cache:
                time.sleep(0.3)
        
        # Guardar cache final
        self._save_cache()
        
        print("=" * 60)
        print(f"✅ Processamento concluído!")
        print(f"   📦 Total processadas: {processadas}")
        print(f"   ✅ Com stock: {com_stock}")
        print(f"   ❌ Erros: {erros}")
        print(f"   💾 Cache hits: {self.stats['cache_hits']}")
        
        if self.token_expired:
            print(f"   ⚠️ TOKEN EXPIRADO - Dimo.py deve renovar e retomar")
        
        # Atualizar stats globais
        self.stats['total'] = processadas
        self.stats['com_stock'] = com_stock
        self.stats['erros'] = erros
        
        return resultado
    
    def _consolidar_stock(self, stocks):
        """
        Consolida os stocks de todas as lojas numa estrutura resumida
        """
        if not stocks:
            return {'total': 0, 'disponivel': 0, 'reservado': 0, 'lojas': [], 'num_lojas': 0}
        
        total = 0
        disponivel = 0
        reservado = 0
        lojas = []
        
        for s in stocks:
            qtd = float(s.get('quantity', 0) or 0)
            avail = float(s.get('availableQuantity', 0) or 0)
            resv = float(s.get('reservedQuantity', 0) or 0)
            
            total += qtd
            disponivel += avail
            reservado += resv
            
            store = s.get('store', {})
            storage_location = s.get('storageLocation', {})
            
            # Adicionar à lista de lojas (apenas as com stock)
            if qtd > 0 or avail > 0:
                lojas.append({
                    'loja': store.get('name', 'Sem loja'),
                    'loja_codigo': store.get('code', ''),
                    'localizacao': storage_location.get('name', '') if storage_location else '',
                    'localizacao_codigo': storage_location.get('code', '') if storage_location else '',
                    'quantidade': qtd,
                    'disponivel': avail,
                    'reservado': resv,
                    'stock_management': s.get('stockManagement', False),
                })
        
        # Ordenar lojas por quantidade (maior primeiro)
        lojas.sort(key=lambda x: x['quantidade'], reverse=True)
        
        return {
            'total': total,
            'disponivel': disponivel,
            'reservado': reservado,
            'lojas': lojas,
            'num_lojas': len([l for l in lojas if l['quantidade'] > 0])
        }
    
    def export_to_csv(self, parts, filename):
        """Exporta para CSV (com stock consolidado)"""
        if not parts:
            print("❌ Sem dados para exportar")
            return False
        
        fieldnames = [
            'code', 'name', 'manufacturerName', 'manufacturerReference',
            'partFamilyName', 'stock_total', 'stock_disponivel',
            'stock_reservado', 'num_lojas', 'criticidade', 'preco', 'id'
        ]
        
        try:
            os.makedirs(os.path.dirname(filename), exist_ok=True)
            
            with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
                writer = csv.DictWriter(f, fieldnames=fieldnames, delimiter=';')
                writer.writeheader()
                
                for p in parts:
                    stock = p.get('stock', {})
                    criticidade = p.get('partCriticality', {})
                    
                    row = {
                        'code': p.get('code', ''),
                        'name': p.get('name', ''),
                        'manufacturerName': p.get('manufacturerName', ''),
                        'manufacturerReference': p.get('manufacturerReference', ''),
                        'partFamilyName': p.get('partFamilyName', ''),
                        'stock_total': stock.get('total', 0),
                        'stock_disponivel': stock.get('disponivel', 0),
                        'stock_reservado': stock.get('reservado', 0),
                        'num_lojas': stock.get('num_lojas', 0),
                        'criticidade': criticidade.get('name', '') if criticidade else '',
                        'preco': p.get('standardPrice', 0),
                        'id': p.get('id', ''),
                    }
                    writer.writerow(row)
            
            print(f"✅ Exportado {len(parts)} peças para:")
            print(f"   📄 {filename}")
            return True
        except Exception as e:
            print(f"❌ Erro ao guardar CSV: {e}")
            return False
    
    def gerar_html_stock(self, parts, filename, refresh_interval=300):
        """Gera HTML visual para o stock"""
        print(f"📝 A gerar HTML do stock: {filename}")
        
        if not parts:
            print("❌ Sem dados para gerar o relatório HTML")
            return False
        
        total_parts = len(parts)
        
        # Estatísticas
        com_stock = sum(1 for p in parts if p.get('stock', {}).get('total', 0) > 0)
        sem_stock = total_parts - com_stock
        
        familias = defaultdict(lambda: {'total': 0, 'com_stock': 0})
        fabricantes = defaultdict(int)
        
        for p in parts:
            familia = p.get('partFamilyName', 'Sem família') or 'Sem família'
            fabricante = p.get('manufacturerName', '') or 'Sem fabricante'
            tem_stock = p.get('stock', {}).get('total', 0) > 0
            
            familias[familia]['total'] += 1
            if tem_stock:
                familias[familia]['com_stock'] += 1
            
            fabricantes[fabricante] += 1
        
        refresh_minutes = refresh_interval // 60 if refresh_interval > 0 else 0
        
        html_content = f'''
<!DOCTYPE html>
<html lang="pt">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>📦 Stock de Peças ({total_parts})</title>
'''
        
        if refresh_interval > 0:
            html_content += f'    <meta http-equiv="refresh" content="{refresh_interval}">\n'
        
        html_content += f'''
    <style>
        * {{ margin: 0; padding: 0; box-sizing: border-box; }}
        body {{ 
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
            min-height: 100vh;
            padding: 20px;
        }}
        .container {{ max-width: 1400px; margin: 0 auto; }}
        
        .header {{
            background: linear-gradient(135deg, #0f3460 0%, #16213e 100%);
            color: white;
            padding: 30px;
            border-radius: 20px;
            margin-bottom: 30px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
        }}
        .header h1 {{ font-size: 32px; margin-bottom: 10px; }}
        .header .subtitle {{ opacity: 0.9; font-size: 16px; }}
        .header .timestamp {{ opacity: 0.7; font-size: 13px; margin-top: 5px; }}
        
        .refresh-info {{
            background: rgba(255,255,255,0.15);
            padding: 6px 15px;
            border-radius: 20px;
            font-size: 13px;
            display: inline-block;
            margin-top: 8px;
        }}
        .refresh-info .countdown {{ font-weight: bold; color: #ffeb3b; }}
        
        .stats {{
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
            gap: 15px;
            margin-top: 20px;
        }}
        .stat-item {{
            background: rgba(255,255,255,0.15);
            padding: 15px;
            border-radius: 12px;
            text-align: center;
        }}
        .stat-item .number {{ font-size: 28px; font-weight: bold; }}
        .stat-item .label {{ font-size: 12px; opacity: 0.8; margin-top: 5px; }}
        .stat-item.com-stock .number {{ color: #4caf50; }}
        .stat-item.sem-stock .number {{ color: #ff9800; }}
        
        .filters {{
            background: white;
            padding: 15px 20px;
            border-radius: 12px;
            margin-bottom: 25px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.08);
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            align-items: center;
        }}
        .filters label {{ font-weight: 600; color: #555; font-size: 14px; }}
        .filters select, .filters input {{
            padding: 8px 12px;
            border: 2px solid #e0e0e0;
            border-radius: 8px;
            font-size: 14px;
        }}
        .filters input:focus, .filters select:focus {{
            outline: none;
            border-color: #0f3460;
        }}
        .btn-refresh {{
            padding: 8px 16px;
            background: #0f3460;
            color: white;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-size: 13px;
        }}
        .filter-stats {{ margin-left: auto; font-size: 13px; color: #666; }}
        
        .part-grid {{
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
            gap: 20px;
        }}
        
        .part-card {{
            background: white;
            border-radius: 16px;
            overflow: hidden;
            box-shadow: 0 4px 15px rgba(0,0,0,0.08);
            border-left: 6px solid #0f3460;
            transition: all 0.3s;
        }}
        .part-card:hover {{
            transform: translateY(-5px);
            box-shadow: 0 8px 30px rgba(0,0,0,0.15);
        }}
        
        .part-card.sem-stock {{
            border-left-color: #ff9800;
            opacity: 0.85;
        }}
        .part-card.com-stock {{
            border-left-color: #4caf50;
        }}
        .part-card.stock-baixo {{
            border-left-color: #f44336;
        }}
        
        .part-card-header {{
            padding: 15px 20px 10px;
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            background: #fafafa;
        }}
        .part-card-header .part-code {{
            font-weight: 700;
            font-size: 15px;
            color: #0f3460;
            font-family: monospace;
        }}
        .part-card-header .stock-badge {{
            font-size: 13px;
            padding: 3px 12px;
            border-radius: 20px;
            font-weight: 700;
        }}
        .stock-badge-ok {{ background: #d4edda; color: #155724; }}
        .stock-badge-baixo {{ background: #fff3cd; color: #856404; }}
        .stock-badge-zero {{ background: #f8d7da; color: #721c24; }}
        
        .part-card-body {{ padding: 15px 20px 20px; }}
        .part-card-body .part-name {{
            font-size: 15px;
            font-weight: 600;
            color: #333;
            margin-bottom: 10px;
        }}
        
        .part-info {{
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 8px;
        }}
        .part-info-item {{
            padding: 6px 10px;
            background: #f8f9fa;
            border-radius: 6px;
        }}
        .part-info-item .label {{
            font-size: 10px;
            color: #888;
            text-transform: uppercase;
            letter-spacing: 0.3px;
            font-weight: 600;
        }}
        .part-info-item .value {{
            font-size: 13px;
            color: #333;
            margin-top: 1px;
            font-weight: 500;
        }}
        .part-info-item .value.code {{
            font-family: monospace;
            font-size: 12px;
        }}
        .part-info-item.full-width {{ grid-column: 1 / -1; }}
        .part-info-item.stock {{ background: #e8f5e9; }}
        .part-info-item.stock-zero {{ background: #ffebee; }}
        
        .lojas-list {{
            margin-top: 10px;
            padding: 10px;
            background: #f8f9fa;
            border-radius: 8px;
        }}
        .lojas-list .title {{
            font-size: 11px;
            color: #666;
            text-transform: uppercase;
            font-weight: 600;
            margin-bottom: 6px;
        }}
        .loja-item {{
            display: flex;
            justify-content: space-between;
            padding: 4px 0;
            font-size: 12px;
            border-bottom: 1px solid #e9ecef;
        }}
        .loja-item:last-child {{ border-bottom: none; }}
        .loja-item .loja-nome {{ color: #555; }}
        .loja-item .loja-qtd {{ font-weight: 700; color: #0f3460; }}
        
        .no-data {{
            text-align: center;
            padding: 60px 20px;
            color: #666;
        }}
        .no-data .icon {{ font-size: 64px; margin-bottom: 20px; }}
        
        .footer {{
            text-align: center;
            margin-top: 30px;
            padding: 20px;
            color: #666;
            font-size: 13px;
        }}
        
        @media (max-width: 768px) {{
            .part-grid {{ grid-template-columns: 1fr; }}
            .part-info {{ grid-template-columns: 1fr; }}
            .header h1 {{ font-size: 24px; }}
            .filters {{ flex-direction: column; align-items: stretch; }}
            .filter-stats {{ margin-left: 0; text-align: center; }}
        }}
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <div style="display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap;">
                <div>
                    <h1>📦 Stock de Peças</h1>
                    <div class="subtitle">Total de {total_parts} peças no sistema</div>
                    <div class="timestamp">📅 Gerado em {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}</div>
'''
        
        if refresh_interval > 0:
            html_content += f'''
                    <div class="refresh-info">
                        🔄 Atualização automática a cada <span class="countdown">{refresh_minutes}</span> minutos
                    </div>
'''
        
        html_content += f'''
                </div>
                <div style="margin-top: 10px;">
                    <button class="btn-refresh" style="background:white;color:#0f3460;"
                            onclick="location.reload()">🔄 Atualizar Agora</button>
                    <button class="btn-refresh" style="background:white;color:#0f3460;"
                            onclick="window.print()">🖨️ Imprimir / PDF</button>
                </div>
            </div>
            
            <div class="stats">
                <div class="stat-item">
                    <div class="number">{total_parts}</div>
                    <div class="label">📦 Total de Peças</div>
                </div>
                <div class="stat-item com-stock">
                    <div class="number">{com_stock}</div>
                    <div class="label">✅ Com Stock</div>
                </div>
                <div class="stat-item sem-stock">
                    <div class="number">{sem_stock}</div>
                    <div class="label">⚠️ Sem Stock</div>
                </div>
                <div class="stat-item">
                    <div class="number">{len(familias)}</div>
                    <div class="label">📂 Famílias</div>
                </div>
            </div>
        </div>
        
        <div class="filters">
            <label>📦 Stock:</label>
            <select id="stockFilter" onchange="filterCards()">
                <option value="all">Todos</option>
                <option value="com">✅ Com Stock</option>
                <option value="sem">⚠️ Sem Stock</option>
            </select>
            
            <label>📂 Família:</label>
            <select id="familyFilter" onchange="filterCards()">
                <option value="all">Todas</option>
'''
        
        for familia in sorted(familias.keys()):
            info = familias[familia]
            html_content += f'                <option value="{familia}">{familia} ({info["total"]})</option>\n'
        
        html_content += '''
            </select>
            
            <label>🔍 Pesquisar:</label>
            <input type="text" id="searchInput" onkeyup="filterCards()" placeholder="Código, nome ou referência...">
            
            <button class="btn-refresh" onclick="clearFilters()" style="background:#f8f9fa;color:#333;">✖️ Limpar</button>
            
            <span class="filter-stats" id="filterStats">Mostrando <span id="visibleCount">0</span> de <span id="totalCount">0</span></span>
        </div>
        
        <div class="part-grid" id="partGrid">
'''
        
        if not parts:
            html_content += '''
            <div class="no-data">
                <div class="icon">📭</div>
                <h2>Nenhuma peça encontrada</h2>
            </div>
        '''
        else:
            for p in parts:
                code = p.get('code', 'N/A')
                name = p.get('name', 'Sem nome')
                familia = p.get('partFamilyName', 'Sem família') or 'Sem família'
                fabricante = p.get('manufacturerName', '') or 'Sem fabricante'
                ref = p.get('manufacturerReference', '') or '-'
                part_id = p.get('id', '')
                
                stock = p.get('stock', {})
                qtd_total = stock.get('total', 0)
                qtd_disponivel = stock.get('disponivel', 0)
                lojas = stock.get('lojas', [])
                alteracoes = p.get('_alteracoes')
                change_badge = ''
                change_description = ''
                if alteracoes:
                    change_badge = '<div class="change-timer" data-change-until="{}">🔔 Alterado</div>'.format(
                        alteracoes['at'] + ChangeTracker.DURACAO_SEGUNDOS
                    )
                    change_description = '<div style="font-size:12px;color:#a55b00;margin-bottom:8px;">🔔 {}</div>'.format(
                        ' • '.join(alteracoes['fields'])
                    )
                
                # Determinar classe e badge
                if qtd_total <= 0:
                    card_class = 'sem-stock'
                    badge_class = 'stock-badge-zero'
                    badge_text = '❌ Sem Stock'
                elif qtd_total <= 2:
                    card_class = 'stock-baixo'
                    badge_class = 'stock-badge-baixo'
                    badge_text = f'⚠️ Baixo ({int(qtd_total)})'
                else:
                    card_class = 'com-stock'
                    badge_class = 'stock-badge-ok'
                    badge_text = f'✅ {int(qtd_total)}'
                if alteracoes:
                    card_class += ' alterado'
                
                # Lista de lojas com stock
                lojas_html = ''
                if lojas:
                    lojas_com_stock = [l for l in lojas if l['quantidade'] > 0]
                    if lojas_com_stock:
                        lojas_html = '<div class="lojas-list"><div class="title">📍 Localizações</div>'
                        for loja in lojas_com_stock[:5]:
                            loc = loja['localizacao'] if loja['localizacao'] else loja['loja']
                            lojas_html += f'''
                            <div class="loja-item">
                                <span class="loja-nome">{loc}</span>
                                <span class="loja-qtd">{int(loja['quantidade'])}</span>
                            </div>'''
                        if len(lojas_com_stock) > 5:
                            lojas_html += f'<div style="text-align:center;font-size:11px;color:#888;margin-top:4px;">+{len(lojas_com_stock) - 5} outras</div>'
                        lojas_html += '</div>'
                
                html_content += f'''
            <div class="part-card {card_class}" style="{'box-shadow: 0 0 0 3px #ff9800, 0 8px 20px rgba(255,152,0,.25);' if alteracoes else ''}"
                 data-stock="{'com' if qtd_total > 0 else 'sem'}"
                 data-family="{familia}"
                 data-search="{code} {name} {ref}">
                <div class="part-card-header">
                    <div>
                        <div class="part-code">{code}</div>
                    </div>
                    <div>
                        <span class="stock-badge {badge_class}">{badge_text}</span>
                        {change_badge}
                    </div>
                </div>
                    <div class="part-card-body">
                    {change_description}
                    <div class="part-name">{name}</div>
                    <div class="part-info">
                        <div class="part-info-item full-width">
                            <div class="label">🏭 Fabricante</div>
                            <div class="value">{fabricante}</div>
                        </div>
                        <div class="part-info-item full-width">
                            <div class="label">🔖 Referência</div>
                            <div class="value code">{ref}</div>
                        </div>
                        <div class="part-info-item stock{'-zero' if qtd_total <= 0 else ''}">
                            <div class="label">📦 Stock Total</div>
                            <div class="value">{int(qtd_total)}</div>
                        </div>
                        <div class="part-info-item">
                            <div class="label">📂 Família</div>
                            <div class="value">{familia}</div>
                        </div>
                    </div>
                    {lojas_html}
                </div>
            </div>
            '''
        
        html_content += f'''
        </div>
        
        <div class="footer">
            <p>📦 Relatório de Stock • Total: {total_parts} peças ({com_stock} com stock) • {datetime.now().strftime('%d/%m/%Y %H:%M:%S')}</p>
        </div>
    </div>
    
    <script>
        function filterCards() {{
            const stockFilter = document.getElementById('stockFilter').value;
            const familyFilter = document.getElementById('familyFilter').value;
            const searchInput = document.getElementById('searchInput').value.toLowerCase().trim();
            const cards = document.querySelectorAll('.part-card');
            let visible = 0;
            
            cards.forEach(card => {{
                const stock = card.dataset.stock || '';
                const family = card.dataset.family || '';
                const search = card.dataset.search || '';
                
                let show = true;
                
                if (stockFilter !== 'all' && stock !== stockFilter) {{
                    show = false;
                }}
                
                if (show && familyFilter !== 'all' && family !== familyFilter) {{
                    show = false;
                }}
                
                if (show && searchInput && !search.toLowerCase().includes(searchInput)) {{
                    show = false;
                }}
                
                if (show) {{
                    card.style.display = 'block';
                    visible++;
                }} else {{
                    card.style.display = 'none';
                }}
            }});
            
            document.getElementById('visibleCount').textContent = visible;
            document.getElementById('totalCount').textContent = cards.length;
        }}
        
        function clearFilters() {{
            document.getElementById('stockFilter').value = 'all';
            document.getElementById('familyFilter').value = 'all';
            document.getElementById('searchInput').value = '';
            filterCards();
        }}

        function updateChangeTimers() {{
            document.querySelectorAll('.change-timer').forEach(function(timer) {{
                const seconds = Math.max(0, Math.ceil(Number(timer.dataset.changeUntil) - Date.now() / 1000));
                const hours = Math.floor(seconds / 3600);
                const minutes = Math.floor((seconds % 3600) / 60);
                const secs = seconds % 60;
                timer.textContent = seconds ? '🔔 Alterado • ' + hours + 'h ' + minutes + 'm ' + secs + 's' : 'Atualize a página';
            }});
        }}
        
        document.addEventListener('DOMContentLoaded', function() {{
            filterCards();
            updateChangeTimers();
            setInterval(updateChangeTimers, 1000);
        }});
    </script>
</body>
</html>
        '''
        
        try:
            os.makedirs(os.path.dirname(filename), exist_ok=True)
            
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(html_content)
            
            print(f"✅ HTML do stock gerado com sucesso!")
            print(f"   📄 {filename}")
            print(f"   📊 Tamanho: {os.path.getsize(filename)} bytes")
            return True
        except Exception as e:
            print(f"❌ Erro ao gerar HTML do stock: {e}")
            import traceback
            traceback.print_exc()
            return False


def obter_stock_completo(limit=None, forcar_atualizacao=False):
    """
    Função principal para obter o stock completo
    Pode ser chamada do Dimo.py
    
    Args:
        limit: número máximo de peças a processar (None = todas)
        forcar_atualizacao: se True, ignora o cache
    """
    DESKTOP = os.path.expanduser("~/Desktop")
    
    print("=" * 70)
    print("📦 EXPORTADOR DE STOCK")
    print("=" * 70)
    
    stock = StockAPI()
    
    if forcar_atualizacao and os.path.exists(stock.cache_file):
        os.remove(stock.cache_file)
        stock.cache = {}
        print("🗑️ Cache apagado.")
    
    if not stock.load_token():
        print("❌ Token não encontrado. Faça login no Dimo.py primeiro.")
        return False
    
    print(f"📦 Cache: {len(stock.cache)} peças já consultadas")
    
    # 1. Buscar lista de peças
    parts = stock.get_all_parts(limit=limit if limit else 10000)
    
    if not parts:
        print("❌ Nenhuma peça encontrada.")
        return False
    
    # Verificar se token expirou durante a busca
    if stock.token_expired:
        print("⚠️ Token expirou durante a busca de peças!")
        return False
    
    # 2. Processar cada peça para obter stock
    parts_com_stock = stock.process_parts_with_stock(
        parts,
        limit=limit,
        save_interval=50
    )
    # Também funciona quando este ficheiro é executado sem o Dimo.py.
    parts_com_stock = ChangeTracker(DESKTOP).analisar("stock", parts_com_stock)
    
    # Verificar se token expirou durante o processamento
    if stock.token_expired:
        print("⚠️ Token expirou durante o processamento!")
        print("   Devolvendo dados parciais...")
        
        # Ainda guardar o que temos
        if parts_com_stock:
            csv_file = os.path.join(DESKTOP, "stock.csv")
            json_file = os.path.join(DESKTOP, "stock.json")
            html_file = os.path.join(DESKTOP, "stock_visual.html")
            
            stock.export_to_csv(parts_com_stock, csv_file)
            
            with open(json_file, 'w', encoding='utf-8') as f:
                json.dump(parts_com_stock, f, ensure_ascii=False, indent=2)
            
            stock.gerar_html_stock(parts_com_stock, html_file, refresh_interval=300)
            
            print(f"   📄 {csv_file}")
            print(f"   📄 {json_file}")
            print(f"   📄 {html_file}")
        
        return False  # Sinalizar que precisa de renovação
    
    # 3. Guardar ficheiros
    csv_file = os.path.join(DESKTOP, "stock.csv")
    json_file = os.path.join(DESKTOP, "stock.json")
    html_file = os.path.join(DESKTOP, "stock_visual.html")
    
    stock.export_to_csv(parts_com_stock, csv_file)
    
    with open(json_file, 'w', encoding='utf-8') as f:
        json.dump(parts_com_stock, f, ensure_ascii=False, indent=2)
    print(f"✅ JSON guardado: {json_file}")
    
    stock.gerar_html_stock(parts_com_stock, html_file, refresh_interval=300)
    
    print("\n" + "=" * 70)
    print("✅ PROCESSO CONCLUÍDO!")
    print(f"   📄 {csv_file}")
    print(f"   📄 {json_file}")
    print(f"   📄 {html_file}")
    print("=" * 70)
    
    return True


if __name__ == "__main__":
    # Se executado diretamente, processa TODAS as peças
    obter_stock_completo(limit=None, forcar_atualizacao=False)
