# Programa: fastsurance_module.py
# Atualizado em: 14 de Agosto de 2025, 13:45 (Horário de São Paulo, Brasil)
# Descrição: Versão com correção do erro de inicialização 'pady_val not defined'.

# Importações necessárias
import ctypes
import os
import subprocess
import sys
import tkinter as tk
from tkinter import messagebox, font, filedialog, ttk
import threading
import queue
import time
import traceback
from datetime import datetime
import json
from typing import Optional, Tuple
import struct
import re
from src.core.port_manager import get_port_manager
from .i18n import get_translator, t
from .app_console import fc_log
from .shell_open import open_path_with_default_app

# Tenta importar as bibliotecas de gráfico.
try:
    from matplotlib.figure import Figure
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
except ImportError:
    _mpl_msg_en = (
        "The 'matplotlib' library was not found.\nPlease install it with the command:\n\npy -m pip install matplotlib"
    )
    try:
        messagebox.showerror(t('fastsurance.missing_dependency'), t('fastsurance.missing_dependency_msg'))
    except Exception:
        try:
            from .i18n import TRANSLATIONS, get_translator
            _trans = get_translator()
            _lc = 'pt' if str((_trans.get_language() if _trans else None) or 'pt').startswith('pt') else 'en'
            _b = TRANSLATIONS.get(_lc, TRANSLATIONS.get('pt', {}))
            messagebox.showerror(
                _b.get('fastsurance.missing_dependency', 'Missing Dependency'),
                _b.get('fastsurance.missing_dependency_msg', _mpl_msg_en),
            )
        except Exception:
            try:
                from .i18n import TRANSLATIONS
                _b = TRANSLATIONS.get('pt', TRANSLATIONS.get('en', {}))
                messagebox.showerror(
                    _b.get('fastsurance.missing_dependency', 'Missing Dependency'),
                    _b.get('fastsurance.missing_dependency_msg', _mpl_msg_en),
                )
            except Exception:
                messagebox.showerror('Missing Dependency', _mpl_msg_en)
    sys.exit()

# --- CONFIGURAÇÃO DA DLL E FUNÇÕES ---
dll_name = "UHFRFID.dll"
try:
    # CORREÇÃO: Caminho robusto para executável e desenvolvimento
    if hasattr(sys, '_MEIPASS'):
        # Modo executável (PyInstaller)
        dll_path = os.path.join(sys._MEIPASS, dll_name)
    else:
        # Modo desenvolvimento
        dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), dll_name)
    
    rfid_sdk = ctypes.CDLL(dll_path)
except OSError as e:
    try:
        messagebox.showerror(
            t('fastsurance.dll_critical_error'),
            t('fastsurance.dll_critical_error_msg').format(dll=dll_name, error=str(e)),
        )
    except Exception:
        try:
            from .i18n import TRANSLATIONS, get_translator
            _trans = get_translator()
            _lc = 'pt' if str((_trans.get_language() if _trans else None) or 'pt').startswith('pt') else 'en'
            _b = TRANSLATIONS.get(_lc, TRANSLATIONS.get('pt', {}))
            _tpl = _b.get(
                'fastsurance.dll_critical_error_msg',
                "Could not load DLL '{dll}'.\nMake sure the file is in the same folder as the program.\n\nReason: {error}",
            )
            messagebox.showerror(
                _b.get('fastsurance.dll_critical_error', 'Critical DLL Error'),
                _tpl.format(dll=dll_name, error=str(e)),
            )
        except Exception:
            try:
                from .i18n import TRANSLATIONS
                _b = TRANSLATIONS.get('pt', TRANSLATIONS.get('en', {}))
                _tpl = _b.get(
                    'fastsurance.dll_critical_error_msg',
                    "Could not load DLL '{dll}'.\nMake sure the file is in the same folder as the program.\n\nReason: {error}",
                )
                messagebox.showerror(
                    _b.get('fastsurance.dll_critical_error', 'Critical DLL Error'),
                    _tpl.format(dll=dll_name, error=str(e)),
                )
            except Exception:
                messagebox.showerror(
                    'Critical DLL Error',
                    f"Could not load DLL '{dll_name}'.\nMake sure the file is in the same folder as the program.\n\nReason: {e}",
                )
    sys.exit()

rfid_sdk.UHF_RFID_Open.argtypes = [ctypes.c_ubyte, ctypes.c_int]; rfid_sdk.UHF_RFID_Open.restype = ctypes.c_int
rfid_sdk.UHF_RFID_Close.argtypes = [ctypes.c_ubyte]; rfid_sdk.UHF_RFID_Close.restype = ctypes.c_int
rfid_sdk.UHF_RFID_Set.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint)]; rfid_sdk.UHF_RFID_Set.restype = ctypes.c_int

# Comandos da DLL
RFID_CMD_GET_TEMPERATURE = 0x34
RFID_CMD_SET_CW_STATUS = 0x24
RFID_CMD_GET_PORT_LOSS = 0x32
RFID_CMD_SET_TXPOWER = 0x10
RFID_CMD_SET_FREQ_TABLE = 0x14
RFID_CMD_INV_TAG = 0x80
RFID_CMD_STOP_INVENTORY = 0x8C  # Parar inventário / limpar estado (igual threshold, populacao)
RFID_CMD_GET_RSSIVALU = 0x64

# --- PARÂMETROS ---
DEFAULT_MAX_POWER_DBM = 25
DEFAULT_MIN_POWER_DBM = 5
DEFAULT_MIN_FREQ_MHZ = 902
DEFAULT_MAX_FREQ_MHZ = 928

INVENTORY_POWER_DBM = 15
HEATING_POWER_DBM = 25
# COM_PORT = 4 ### ALTERADO: Removido
BAUD_RATE = 115200
REFLECTED_POWER_LIMIT = 10.0  # dBm - Potência refletida máxima aceitável
TEMPERATURE_LIMIT = 50.0

RxAdcTable = [
    0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0005, 0x0007, 0x000B, 0x0010, 0x0116, 0x011D, 0x0126,
    0x0131, 0x013E, 0x024C, 0x0260, 0x0374, 0x048B, 0x05A5, 0x06C3, 0x08E6, 0x09FF, 0x0BFF, 0x0EFF,
    0x10FF, 0x14FF, 0x17FF, 0x1CFF, 0x21FF, 0x26FF, 0x2DFF, 0x34FF, 0x3CFF, 0x46FF, 0x50FF, 0x5DFF,
    0x6AFF, 0x7AFF, 0x8BFF, 0x9EFF, 0xB4FF, 0xCCFF, 0xE7FF, 0xFFFF
]

class RFIDApp(tk.Frame):
    """Módulo FastSurance. O histórico de testes segue um modelo próprio (sem coluna
    Plot/checkbox como em Threshold/RSSI); alinhar às outras abas exigiria decisão de produto."""

    ### ALTERADO: Adicionado com_port=4 ###
    def __init__(self, master, hardware_controller=None, demo_mode=False, com_port=4, app_shell=None):
        super().__init__(master)
        self.master = master
        self.hardware_controller = hardware_controller
        self.demo_mode = demo_mode
        self.com_port = com_port ### NOVO: Armazena a porta COM ###
        self.app_shell = app_shell  # CORREÇÃO: Aceita app_shell como parâmetro
        self.port_manager = get_port_manager()
        
        self.root_window = self.master.winfo_toplevel()
        # Em AppShell, não sobrescreve o título global da aplicação ao trocar de aba.
        if self.app_shell is None and self.root_window == self.master:
            self.root_window.title(t('fastsurance.title'))
        
        self.grid(row=0, column=0, sticky="nsew")
        self.master.columnconfigure(0, weight=0)
        self.master.columnconfigure(1, weight=1)
        self.master.rowconfigure(0, weight=1)

        self.module_name = "FastSurance"
        self.blinking_job_id = None
        
        # Sistema de tradução
        self.translator = get_translator()
        self._widget_refs = {}  # Para armazenar referências aos widgets para atualização de idioma
        # Registra este módulo para receber notificações de mudança de idioma
        self.translator.add_language_change_listener(self._on_language_changed)
        
        self.pady_val = 3

        # Removido popup de confirmação ao sair (WM_DELETE_WINDOW)
        
        # NOVO: Também captura o evento de destruição do frame
        self.bind("<Destroy>", self._on_frame_destroy)

        # Inicialização da licença movida para depois do app_shell ser configurado
        self.license_limits = {'min_freq': DEFAULT_MIN_FREQ_MHZ, 'max_freq': DEFAULT_MAX_FREQ_MHZ, 'min_power': DEFAULT_MIN_POWER_DBM, 'max_power': DEFAULT_MAX_POWER_DBM, 'is_licensed': False}

        self.results_queue = queue.Queue()
        self.scan_parameters = []
        self.available_tags, self.selected_epc = [], None
        self.last_tag_test_data, self.last_threshold_test_data = None, None
        
        # Estado de execução/cancelamento para Threshold
        self.threshold_test_running = False
        self.threshold_cancel_requested = False
        
        # Margem de segurança individual por teste (Go/NoGo)
        self.safety_margin_vars = [tk.DoubleVar(value=0.2) for _ in range(5)]

        # NOVO: Sistema de persistência de dados
        self.db_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fastsurance_db.json")
        self.all_tag_test_data, self.test_history = [], []
        self.tags_testadas, self.tags_aprovadas, self.tags_reprovadas = 0, 0, 0
        
        # NOVO: Inicializa variáveis de threshold ANTES de carregar dados persistentes
        self.threshold_graph_data = []
        
        # Carrega dados persistentes DEPOIS de inicializar as variáveis
        self._load_persistent_data()
        self.tags_testadas_var = tk.StringVar(value=""); self.tags_aprovadas_var = tk.StringVar(value="")
        self.tags_reprovadas_var = tk.StringVar(value=""); self.yield_var = tk.StringVar(value="")
        
        # NOVO: Variáveis para controle de ordenação
        self.current_sort_column = None
        self.current_sort_reverse = False
        
        self._create_widgets()
        self.update_ui_for_test_type()
        self.process_queue()
        
        # Chama refresh_language após a UI estar pronta para garantir que o título seja traduzido
        self.after(100, self.refresh_language)
        
        # NOVO: Restaura dados persistentes na interface após criar widgets
        self.after(1000, self._restore_persistent_interface)
        self.after(3000, self._restore_persistent_interface)  # Backup - segunda tentativa
        
        # NOVO: Restaura frequências dos testes de tag no início (se o modo padrão for TAG_TESTS)
        self.after(2000, self._restore_tag_test_frequencies)
        self.after(4000, self._restore_tag_test_frequencies)  # Backup - segunda tentativa
        
        # NOVO: Inicia salvamento automático periódico
        self._start_auto_save()
        
        # Licença / app_shell (evita depender só do destroy, que agendava isso por engano)
        self.after(100, self._check_app_shell_availability)

        # Com app_shell, navigate_to agenda _initialize_hardware_on_enter após force_reset.
        # Standalone (__main__): agenda aqui.
        if not self.app_shell:
            self.after(400, self._initialize_hardware_on_enter)

    def _initialize_hardware_on_enter(self):
        """REQUISITOS 1+4: Captura porta e reseta hardware"""
        if not rfid_sdk:
            return
        
        try:
            if self.app_shell and getattr(self.app_shell, 'com_port', None) is not None:
                self.com_port = self.app_shell.com_port
            pm = getattr(self, 'port_manager', None) or get_port_manager()
            self.port_manager = pm
            if pm:
                pm.com_port = self.com_port

            fc_log("fastsurance", f"🔧 FastSurance: Inicializando hardware (COM{self.com_port})...")
            if self.port_manager.acquire_port("FastSurance", timeout=2.0):
                try:
                    if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                        fc_log("fastsurance", f"✅ FastSurance: Porta COM{self.com_port} capturada")
                        
                        out_buf = ctypes.create_string_buffer(64)
                        out_len = ctypes.c_uint(0)
                        rfid_sdk.UHF_RFID_Set(RFID_CMD_STOP_INVENTORY, None, 0, out_buf, ctypes.byref(out_len))
                        
                        rfid_sdk.UHF_RFID_Close(self.com_port)
                        fc_log("fastsurance", f"✅ FastSurance: Hardware inicializado (sem bip)")
                finally:
                    self.port_manager.release_port("FastSurance")
        except Exception as e:
            fc_log("fastsurance", f"⚠️ FastSurance: Erro: {e}")
    
    def destroy(self):
        """REQUISITOS 2+3+4: Libera porta, para comandos, reseta"""
        try:
            # Remove listener de mudança de idioma
            if hasattr(self, 'translator') and self.translator:
                try:
                    self.translator.remove_language_change_listener(self._on_language_changed)
                except:
                    pass
            
            # NOVO: Salva dados persistentes antes de fazer cleanup
            fc_log("fastsurance", f"🔄 FastSurance: Salvando dados antes de destruir...")
            try:
                self._save_persistent_data()
            except Exception as e:
                fc_log("fastsurance", f"⚠️ FastSurance: Erro ao salvar dados no destroy: {e}")
            
            fc_log("fastsurance", f"🔄 FastSurance: Iniciando cleanup...")
            
            if hasattr(self, 'test_running') and self.test_running:
                self.test_running = False
            
            if rfid_sdk:
                try:
                    if self.port_manager.acquire_port("FastSurance", timeout=2.0):
                        try:
                            if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                                out_buf = ctypes.create_string_buffer(64)
                                out_len = ctypes.c_uint(0)
                                rfid_sdk.UHF_RFID_Set(RFID_CMD_STOP_INVENTORY, None, 0, out_buf, ctypes.byref(out_len))
                            
                            rfid_sdk.UHF_RFID_Close(self.com_port)
                            fc_log("fastsurance", f"✅ FastSurance: Comandos parados, porta liberada (sem bip)")
                        finally:
                            self.port_manager.release_port("FastSurance")
                except Exception as e:
                    fc_log("fastsurance", f"⚠️ FastSurance: Erro: {e}")
            
            fc_log("fastsurance", f"✅ FastSurance: Cleanup concluído")
        except:
            pass
        
        try:
            super().destroy()
        except:
            pass

    def _update_license_limits_from_app_shell(self):
        """Atualiza limites de licença a partir do app_shell - CORREÇÃO PARA EVITAR LOOPS INFINITOS"""
        try:
            # CORREÇÃO: Evita loops infinitos verificando se já foi atualizado
            if hasattr(self, '_license_update_in_progress') and self._license_update_in_progress:
                return
            
            self._license_update_in_progress = True
            
            # CORREÇÃO: Verifica se o app_shell está disponível
            if not hasattr(self, 'app_shell') or self.app_shell is None:
                self._license_update_in_progress = False
                return
            
            # CORREÇÃO: Verifica se a licença mudou antes de atualizar
            current_license = getattr(self.app_shell, 'active_license_token', None)
            if hasattr(self, '_last_license_hash') and self._last_license_hash == hash(str(current_license)):
                self._license_update_in_progress = False
                return
            
            valid_lics = ["FastSurance", "FastChecker"]
            license_limits = self.app_shell._calculate_license_limits(valid_lics)
            
            if license_limits.get('is_licensed', False):
                self.license_limits = license_limits
                self._update_power_scales()
                self.update_ui_state()
            else:
                self.license_limits = {'min_freq': DEFAULT_MIN_FREQ_MHZ, 'max_freq': DEFAULT_MAX_FREQ_MHZ, 'min_power': DEFAULT_MIN_POWER_DBM, 'max_power': DEFAULT_MAX_POWER_DBM, 'is_licensed': False}
                self.update_ui_state()
            
            # CORREÇÃO: Atualiza hash da licença para evitar loops
            self._last_license_hash = hash(str(current_license))
            
        except Exception as e:
            self.license_limits = {'min_freq': DEFAULT_MIN_FREQ_MHZ, 'max_freq': DEFAULT_MAX_FREQ_MHZ, 'min_power': DEFAULT_MIN_POWER_DBM, 'max_power': DEFAULT_MAX_POWER_DBM, 'is_licensed': False}
            self.update_ui_state()
        finally:
            # CORREÇÃO: Sempre libera o flag de atualização
            self._license_update_in_progress = False

    def _update_power_scales(self):
        try:
            max_power = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
            min_power = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
            
            for i, scale in enumerate(self.power_scales):
                scale.config(from_=min_power, to=max_power)
                current_value = self.power_vars[i].get()
                if current_value > max_power:
                    self.power_vars[i].set(max_power)
                elif current_value < min_power:
                    self.power_vars[i].set(min_power)
            
            pass
        except Exception as e:
            pass

    def _check_app_shell_availability(self):
        """
        CORREÇÃO: Verifica periodicamente se o app_shell está disponível
        e atualiza a licença quando possível - EVITA VERIFICAÇÕES INFINITAS
        """
        # CORREÇÃO: Limita o número de tentativas para evitar loops infinitos
        if not hasattr(self, '_app_shell_check_count'):
            self._app_shell_check_count = 0
        
        if self._app_shell_check_count >= 50:  # Máximo 50 tentativas (5 segundos)
            return
        
        if hasattr(self, 'app_shell') and self.app_shell:
            # CORREÇÃO: Inicializa port_manager se disponível
            if hasattr(self.app_shell, 'port_manager') and not hasattr(self, 'port_manager'):
                self.port_manager = self.app_shell.port_manager
            
            self._update_license_limits_from_app_shell()
            self._app_shell_check_count = 0  # Reset contador
        else:
            # Incrementa contador e agenda nova verificação
            self._app_shell_check_count += 1
            self.after(100, self._check_app_shell_availability)

    def update_ui_state(self):
        """
        Atualiza o estado dos botões baseado na licença - CORREÇÃO PARA SEMPRE HABILITAR COM LICENÇA
        """
        try:
            is_licensed = self.license_limits.get('is_licensed', False)
            
            
            # CORREÇÃO: Atualiza a mensagem de modo browser
            self._update_browser_mode_message(is_licensed)
            
            # CORREÇÃO: Botões "Importar" e "Limpar" sempre habilitados
            self.import_button.config(state='normal')
            self.clear_all_button.config(state='normal')
            
            # CORREÇÃO: Botões de execução baseados na licença
            if is_licensed:
                # Com licença, os botões ficam SEMPRE habilitados
                self.start_button.config(state='normal')
                self.scan_tags_button.config(state='normal')
                
                
                # CORREÇÃO: Garante que o texto do botão seja correto para o tipo de teste atual
                test_type = self.test_type_var.get()
                if test_type == "TAG_TESTS":
                    self.start_button.config(text=t('fastsurance.testing'))
                elif test_type == "THRESHOLD_TEST":
                    self.start_button.config(text=t('fastsurance.testing'))
                else:
                    self.start_button.config(text=t('fastsurance.start_execution'))
                
                # CORREÇÃO: Sliders de potência habilitados com licença
                for power_scale in self.power_scales:
                    power_scale.config(state='normal')
                    
                # CORREÇÃO: Entradas de frequência habilitadas com licença
                for entry in self.tag_test_freq_entries:
                    entry.config(state='normal')
                    
                # CORREÇÃO: Entradas de frequência de threshold habilitadas com licença
                for entry in self.threshold_freq_entries:
                    entry.config(state='normal')
                    
            else:
                # Sem licença, os botões de execução ficam desabilitados
                self.start_button.config(state='disabled')
                self.scan_tags_button.config(state='disabled')
                
                
                # CORREÇÃO: Sliders de potência desabilitados sem licença
                for power_scale in self.power_scales:
                    power_scale.config(state='disabled')
                    
                # CORREÇÃO: Entradas de frequência desabilitadas sem licença
                for entry in self.tag_test_freq_entries:
                    entry.config(state='disabled')
                    
                # CORREÇÃO: Entradas de frequência de threshold desabilitadas sem licença
                for entry in self.threshold_freq_entries:
                    entry.config(state='disabled')
                
        except Exception as e:
            import traceback
            traceback.print_exc()

    def _update_browser_mode_message(self, is_licensed):
        """CORREÇÃO: Atualiza a mensagem de modo browser baseada no status da licença"""
        try:
            # CORREÇÃO: Verifica se os widgets existem antes de tentar acessá-los
            if not hasattr(self, 'input_frame') or self.input_frame is None:
                return
            
            if not is_licensed and self.browser_frame is None:
                # Cria a mensagem de modo browser
                self.browser_frame = tk.Frame(self.input_frame)
                self.browser_frame.grid(row=6, column=0, columnspan=6, sticky="ew", pady=(5, 5), padx=5)
                
                bullet_label = tk.Label(self.browser_frame, text="•", foreground="blue", font=("Helvetica", 10))
                bullet_label.pack(side='left', anchor='w')
                
                browser_mode_label = tk.Label(self.browser_frame, text=t('fastsurance.browser_mode'), foreground="blue", font=("Helvetica", 10))
                browser_mode_label.pack(side='left', anchor='w')
                
                
            elif is_licensed and self.browser_frame is not None:
                # Remove a mensagem de modo browser
                self.browser_frame.destroy()
                self.browser_frame = None
                
        except Exception as e:
            import traceback
            traceback.print_exc()

    def show_help_window(self, title, file_path):
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.readlines()
        except FileNotFoundError:
            messagebox.showerror(t('fastsurance.error'), t('fastsurance.help_file_not_found').format(file=file_path))
            return

        help_win = tk.Toplevel(self.master)
        help_win.title(title)
        help_win.geometry("600x500")
        help_win.transient(self.master)
        help_win.grab_set()

        text_frame = tk.Frame(help_win)
        text_frame.pack(fill="both", expand=True, padx=5, pady=5)
        text_frame.rowconfigure(0, weight=1)
        text_frame.columnconfigure(0, weight=1)
        
        help_text = tk.Text(text_frame, wrap=tk.WORD, padx=10, pady=10, relief=tk.FLAT, font=("Helvetica", 9))
        scrollbar = ttk.Scrollbar(text_frame, orient="vertical", command=help_text.yview)
        help_text.configure(yscrollcommand=scrollbar.set)

        help_text.grid(row=0, column=0, sticky="nsew")
        scrollbar.grid(row=0, column=1, sticky="ns")

        h1_font = font.Font(family="Helvetica", size=14, weight="bold")
        h2_font = font.Font(family="Helvetica", size=11, weight="bold")
        bold_font = font.Font(family="Helvetica", size=9, weight="bold")

        help_text.tag_configure("h1", font=h1_font, spacing3=10)
        help_text.tag_configure("h2", font=h2_font, spacing1=15, spacing3=5)
        help_text.tag_configure("bold", font=bold_font)
        
        for line in content:
            clean_line = re.sub(r'\\s*', '', line).strip()
            if not clean_line:
                help_text.insert(tk.END, "\n")
                continue
            
            if clean_line.startswith('[h1]'):
                text = clean_line.replace('[h1]', '').replace('[/h1]', '').strip()
                help_text.insert(tk.END, text + "\n", "h1")
            elif clean_line.startswith('[h2]'):
                text = clean_line.replace('[h2]', '').replace('[/h2]', '').strip()
                help_text.insert(tk.END, text + "\n", "h2")
            elif '[b]' in clean_line and '[/b]' in clean_line:
                before, rest = clean_line.split('[b]', 1)
                bold_text, after = rest.split('[/b]', 1)
                help_text.insert(tk.END, before)
                help_text.insert(tk.END, bold_text)
                help_text.insert(tk.END, after + "\n")
            else:
                help_text.insert(tk.END, clean_line + "\n")

        help_text.config(state='disabled')

    def _create_widgets(self):
        self.config_frame = tk.Frame(self)
        self.config_frame.grid(row=0, column=0, sticky="nsew", padx=10, pady=10)
        self.config_frame.columnconfigure(0, weight=1)
        self.config_frame.rowconfigure(5, weight=1)
        
        safety_frame = tk.LabelFrame(self.config_frame, text=t('fastsurance.safety_system'), padx=10, pady=5, font=("Helvetica", 10))
        safety_frame.grid(row=0, column=0, sticky="ew", pady=(0,5))
        safety_frame.columnconfigure(3, weight=1)
        self._widget_refs['safety_frame'] = safety_frame

        self.safety_status_label = tk.Label(safety_frame, text="", fg="green", font=("Helvetica", 9))
        self.safety_status_label.grid(row=0, column=0, columnspan=5, sticky='w', padx=(0, 5))
        
        self.temperature_label_text = tk.Label(safety_frame, text=t('fastsurance.temperature'), fg="black")
        self.temperature_label_text.grid(row=1, column=0, sticky='w', padx=(0, 5))
        self._widget_refs['temperature_label_text'] = self.temperature_label_text
        self.temperature_label = tk.Label(safety_frame, text="--°C", fg="gray", font=("Helvetica", 10))
        self.temperature_label.grid(row=1, column=1, sticky='w')
        
        self.vswr_label_text = tk.Label(safety_frame, text=t('fastsurance.vswr'), fg="black")
        self.vswr_label_text.grid(row=1, column=2, sticky='w', padx=(15, 5))
        self._widget_refs['vswr_label_text'] = self.vswr_label_text
        self.vswr_label = tk.Label(safety_frame, text="--", fg="gray", font=("Helvetica", 10))
        self.vswr_label.grid(row=1, column=3, sticky='w')
        self.vswr_status_label = tk.Label(safety_frame, text="", fg="gray", font=("Helvetica", 8))
        self.vswr_status_label.grid(row=1, column=4, sticky='w', padx=(5,0))

        self.epc_filter_frame = tk.LabelFrame(self.config_frame, text=t('fastsurance.epc_filter'), padx=10, pady=5, font=("Helvetica", 10))
        self.epc_filter_frame.grid(row=1, column=0, sticky="ew", pady=(0,5))
        self._widget_refs['epc_filter_frame'] = self.epc_filter_frame
        self.scan_tags_button = tk.Button(self.epc_filter_frame, text=t('fastsurance.tag_registration'), command=self.scan_available_tags, width=20)
        self.scan_tags_button.pack(side="left", padx=(0, 10))
        self._widget_refs['scan_tags_button'] = self.scan_tags_button
        selected_label = tk.Label(self.epc_filter_frame, text=t('fastsurance.selected_epc'), font=("Helvetica", 9))
        selected_label.pack(side="left", padx=(0, 5))
        self._widget_refs['selected_epc_label'] = selected_label
        self.epc_combobox = ttk.Combobox(self.epc_filter_frame, width=45, state="readonly"); self.epc_combobox.pack(side="left", padx=(0, 10)); self.epc_combobox.bind("<<ComboboxSelected>>", self.on_epc_selected); self.epc_combobox.set(t('fastsurance.click_to_load'))
        self.epc_status_label = tk.Label(self.epc_filter_frame, text=t('fastsurance.no_epc_selected'), fg="gray", font=("Helvetica", 8)); self.epc_status_label.pack(side="left", padx=(10, 0))
        self._widget_refs['epc_status_label'] = self.epc_status_label
        
        test_type_frame = tk.LabelFrame(self.config_frame, text=t('fastsurance.test_type'), padx=10, pady=5, font=("Helvetica", 10))
        test_type_frame.grid(row=2, column=0, sticky="ew", pady=(0,5))
        self._widget_refs['test_type_frame'] = test_type_frame
        self.test_type_var = tk.StringVar(value="TAG_TESTS")  ### CORREÇÃO: Go/NoGo como padrão
        
        # Frame para organizar os controles à esquerda
        test_type_left_frame = tk.Frame(test_type_frame)
        test_type_left_frame.pack(side="left", fill="y")
        
        radio_gonogo = tk.Radiobutton(test_type_left_frame, text=t('fastsurance.gonogo'), variable=self.test_type_var, value="TAG_TESTS", command=self.update_ui_for_test_type)
        radio_gonogo.pack(side="left", padx=(10,0))
        self._widget_refs['radio_gonogo'] = radio_gonogo

        radio_threshold = tk.Radiobutton(test_type_left_frame, text=t('fastsurance.threshold'), variable=self.test_type_var, value="THRESHOLD_TEST", command=self.update_ui_for_test_type)
        radio_threshold.pack(side="left", padx=10)
        self._widget_refs['radio_threshold'] = radio_threshold

        self.chip_id_button = tk.Button(
            test_type_frame,
            text=t("fastsurance.chip_id_button"),
            command=self._launch_chip_id_standalone,
            font=("Helvetica", 9),
        )
        self._widget_refs["chip_id_button"] = self.chip_id_button

        # NOVO: Nota "Use atenuador" à direita (o botão Chip_ID fica à sua esquerda no modo Threshold)
        self.attenuator_note = tk.Label(test_type_frame, text=t('fastsurance.use_attenuator'), font=("Helvetica", 9, "italic"), fg="#e74c3c")
        self.attenuator_note.pack(side="right", padx=(0, 10))
        self._widget_refs['attenuator_note'] = self.attenuator_note

        self.input_frame = tk.LabelFrame(self.config_frame, text=t('fastsurance.test_config'), padx=10, pady=10, font=("Helvetica", 10))
        self.input_frame.grid(row=3, column=0, sticky="ew", pady=(0,5))
        self._widget_refs['input_frame'] = self.input_frame
        freq_label = tk.Label(self.input_frame, text=t('fastsurance.freq_mhz'), font=("Helvetica", 10))
        freq_label.grid(row=0, column=1, padx=5)
        self._widget_refs['freq_label'] = freq_label
        self.power_label = tk.Label(self.input_frame, text=t('fastsurance.threshold_dbm'), font=("Helvetica", 10)); self.power_label.grid(row=0, column=2, columnspan=2, padx=5)
        self._widget_refs['power_label'] = self.power_label
        margin_label = tk.Label(self.input_frame, text=t('fastsurance.margin_dbm'), font=("Helvetica", 10))
        margin_label.grid(row=0, column=4, padx=(10, 5), sticky="e")
        self._widget_refs['margin_label'] = margin_label
        self.tag_test_freq_entries, self.threshold_freq_entries, self.power_vars, self.power_scales, self.power_display_labels, self.threshold_power_entries, self.safety_margin_entries = [],[],[],[],[],[],[]
        for i in range(5):
            test_label = tk.Label(self.input_frame, text=f"{t('fastsurance.test')} {i+1}:")
            test_label.grid(row=i+1, column=0, padx=5, pady=2, sticky="w")
            self._widget_refs[f'test_label_{i}'] = test_label
            tag_freq_entry = tk.Entry(self.input_frame, width=10, justify='center')
            # NOVO: Salva automaticamente quando o campo perder o foco ou Enter for pressionado
            def save_on_focus_out(event, entry_idx=i):
                self.after(100, self._save_persistent_data)
                return None
            def save_on_return(event, entry_idx=i):
                self.after(100, self._save_persistent_data)
                return None
            tag_freq_entry.bind('<FocusOut>', save_on_focus_out)
            tag_freq_entry.bind('<Return>', save_on_return)
            self.tag_test_freq_entries.append(tag_freq_entry)
            
            threshold_freq_entry = tk.Entry(self.input_frame, width=10, justify='center')
            # NOVO: Salva automaticamente quando o campo perder o foco ou Enter for pressionado
            def save_threshold_on_focus_out(event, entry_idx=i):
                self.after(100, self._save_persistent_data)
                return None
            def save_threshold_on_return(event, entry_idx=i):
                self.after(100, self._save_persistent_data)
                return None
            threshold_freq_entry.bind('<FocusOut>', save_threshold_on_focus_out)
            threshold_freq_entry.bind('<Return>', save_threshold_on_return)
            self.threshold_freq_entries.append(threshold_freq_entry)
            
        # CORREÇÃO: Campos vazios para configuração limpa
        # Não insere valores padrão - campos ficam vazios
            
            # CORREÇÃO: Usa valores padrão se license_limits ainda não estiver disponível
            max_power = getattr(self, 'license_limits', {}).get('max_power', DEFAULT_MAX_POWER_DBM)
            min_power = getattr(self, 'license_limits', {}).get('min_power', DEFAULT_MIN_POWER_DBM)
            power_var = tk.DoubleVar(value=max_power); self.power_vars.append(power_var)
            power_display_label = tk.Label(self.input_frame, text=f"{power_var.get():.1f} dBm", width=9, anchor="w"); self.power_display_labels.append(power_display_label)
            def update_power_display(val, label=power_display_label): label.config(text=f"{float(val):.1f} dBm")
            power_scale = tk.Scale(self.input_frame, from_=min_power, to=max_power, orient=tk.HORIZONTAL, resolution=0.5, length=180, showvalue=0, variable=power_var, command=update_power_display); self.power_scales.append(power_scale)
            
            safety_margin_entry = tk.Entry(self.input_frame, textvariable=self.safety_margin_vars[i], width=8, justify='center')
            safety_margin_entry.grid(row=i+1, column=4, padx=(10, 5), sticky="w")
            def save_margin_on_focus_out(event, entry_idx=i):
                self.after(100, self._save_persistent_data)
                return None
            def save_margin_on_return(event, entry_idx=i):
                self.after(100, self._save_persistent_data)
                return None
            safety_margin_entry.bind('<FocusOut>', save_margin_on_focus_out)
            safety_margin_entry.bind('<Return>', save_margin_on_return)
            self.safety_margin_entries.append(safety_margin_entry)
            self._widget_refs[f'safety_margin_entry_{i}'] = safety_margin_entry
        
        command_button_frame = tk.Frame(self.input_frame); command_button_frame.grid(row=0, column=5, rowspan=6, padx=20, sticky="ns")
        
        self.start_button = tk.Button(command_button_frame, text=t('fastsurance.start_execution'), command=self.start_scan_thread, font=("Helvetica", 10), width=15); self.start_button.pack(pady=self.pady_val, fill="x")
        self._widget_refs['start_button'] = self.start_button
        self.clear_all_button = tk.Button(command_button_frame, text=t('fastsurance.clear'), command=self.clear_all_data, width=15); self.clear_all_button.pack(pady=self.pady_val, fill="x")
        self._widget_refs['clear_all_button'] = self.clear_all_button
        self.save_button = tk.Button(command_button_frame, text=t('fastsurance.save'), command=self.save_report_to_file, width=15); self.save_button.pack(pady=self.pady_val, fill="x")
        self._widget_refs['save_button'] = self.save_button
        self.import_button = tk.Button(command_button_frame, text=t('fastsurance.import'), command=self.import_report_from_file, width=15); self.import_button.pack(pady=self.pady_val, fill="x")
        self._widget_refs['import_button'] = self.import_button
        self.migrate_button = tk.Button(command_button_frame, text=t('fastsurance.import_from_threshold'), command=self.migrate_threshold_data, width=15); self.migrate_button.pack(pady=self.pady_val, fill="x")
        self._widget_refs['migrate_button'] = self.migrate_button
        self.threshold_pdf_button = tk.Button(command_button_frame, text=t('fastsurance.report_pdf'), command=self.generate_threshold_pdf_report, width=15); self.threshold_pdf_button.pack(pady=self.pady_val, fill="x")
        self._widget_refs['threshold_pdf_button'] = self.threshold_pdf_button
        self.tags_pdf_button = tk.Button(command_button_frame, text=t('fastsurance.report_pdf'), command=self.generate_tags_pdf_report, width=15); self.tags_pdf_button.pack(pady=self.pady_val, fill="x")
        self._widget_refs['tags_pdf_button'] = self.tags_pdf_button
        
        # NOVO: Frame para mensagem de modo browser
        self.browser_frame = None
        
        # CORREÇÃO: Inicializa port_manager se disponível
        if hasattr(self, 'app_shell') and self.app_shell and hasattr(self.app_shell, 'port_manager'):
            self.port_manager = self.app_shell.port_manager
            fc_log("fastsurance", f"✅ FASTSURANCE: Port_manager inicializado via app_shell")
        else:
            self.port_manager = None
            fc_log("fastsurance", f"⚠️ FASTSURANCE: Port_manager não disponível, usando fallback")
        
        # CORREÇÃO: update_ui_state() será chamado após todos os widgets serem criados

        self.summary_frame = tk.LabelFrame(self.input_frame, text=t('fastsurance.execution_summary'), padx=10, pady=5, font=("Helvetica", 9)); self.summary_frame.grid(row=0, column=6, rowspan=6, padx=(20, 10), pady=5, sticky="ns")
        self._widget_refs['summary_frame'] = self.summary_frame
        tags_tested_label = tk.Label(self.summary_frame, text=t('fastsurance.tags_tested')); tags_tested_label.grid(row=0, column=0, sticky="w"); self._widget_refs['tags_tested_label'] = tags_tested_label
        tk.Label(self.summary_frame, textvariable=self.tags_testadas_var, font=("Helvetica", 10)).grid(row=0, column=1, sticky="e", padx=5)
        approved_label = tk.Label(self.summary_frame, text=t('fastsurance.approved')); approved_label.grid(row=1, column=0, sticky="w"); self._widget_refs['approved_label'] = approved_label
        tk.Label(self.summary_frame, textvariable=self.tags_aprovadas_var, font=("Helvetica", 10), fg="#27ae60").grid(row=1, column=1, sticky="e", padx=5)
        rejected_label = tk.Label(self.summary_frame, text=t('fastsurance.rejected')); rejected_label.grid(row=2, column=0, sticky="w"); self._widget_refs['rejected_label'] = rejected_label
        tk.Label(self.summary_frame, textvariable=self.tags_reprovadas_var, font=("Helvetica", 10), fg="#c0392b").grid(row=2, column=1, sticky="e", padx=5)
        yield_label = tk.Label(self.summary_frame, text=t('fastsurance.yield')); yield_label.grid(row=3, column=0, sticky="w"); self._widget_refs['yield_label'] = yield_label
        tk.Label(self.summary_frame, textvariable=self.yield_var, font=("Helvetica", 10)).grid(row=3, column=1, sticky="e", padx=5)
        self.input_frame.columnconfigure(6, weight=1)
        
        # NOVO: Gráfico de Threshold
        self.graph_frame = tk.LabelFrame(self.config_frame, text=t('fastsurance.graph'), padx=10, pady=5, font=("Helvetica", 10))
        self._widget_refs['graph_frame'] = self.graph_frame
        self.graph_frame.grid(row=4, column=0, sticky="ew", pady=(5,0))
        
        # Cria figura do matplotlib
        self.threshold_fig = Figure(figsize=(8, 4), dpi=100)
        self.threshold_ax = self.threshold_fig.add_subplot(111)
        self.threshold_ax.set_xlabel(t('fastsurance.frequency_label'))
        self.threshold_ax.set_ylabel(t('fastsurance.threshold_label'))
        self.threshold_ax.set_title('')
        self.threshold_ax.set_ylim(5, 25)
        self.threshold_ax.grid(True, alpha=0.3)
        
        # Canvas para o gráfico
        self.threshold_canvas = FigureCanvasTkAgg(self.threshold_fig, self.graph_frame)
        self.threshold_canvas.draw()
        self.threshold_canvas.get_tk_widget().pack(fill="both", expand=True)
        
        
        
        # Cria tooltip annotation para o gráfico de threshold
        self._create_tooltip_annotation()
        
        # Conecta eventos de mouse ao gráfico
        try:
            motion_id = self.threshold_canvas.mpl_connect("motion_notify_event", self.on_mouse_move)
            leave_id = self.threshold_canvas.mpl_connect("axes_leave_event", self.on_mouse_leave)
            fc_log("fastsurance", f"✅ Eventos de mouse conectados: motion_id={motion_id}, leave_id={leave_id}")
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao conectar eventos de mouse: {e}")
        
        # Armazena dados da curva para o tooltip
        self.threshold_data = {'freq': [], 'threshold': []}
        
        
        self.history_container = tk.LabelFrame(self.config_frame, text=t('fastsurance.test_history'), padx=10, pady=10, font=("Helvetica", 10)); self.history_container.grid(row=5, column=0, sticky="nsew", pady=(5,0)); self.history_container.rowconfigure(0, weight=1); self.history_container.columnconfigure(0, weight=1)
        self._widget_refs['history_container'] = self.history_container
        
        cols = ('EPC', 'Resultado', 'Falhas Freq.', 'Data/Hora')
        self.history_tree = ttk.Treeview(self.history_container, columns=cols, show='headings', height=25)
        
        # NOVO: Headers com comandos de ordenação
        self.history_tree.heading('EPC', text=f"{t('fastsurance.epc_column')} ↕", command=lambda: self.sort_treeview('EPC')); self.history_tree.column('EPC', width=200, anchor='w')
        self._widget_refs['history_tree_epc'] = self.history_tree
        self.history_tree.heading('Resultado', text=f"{t('fastsurance.result_column')} ↕", command=lambda: self.sort_treeview('Resultado')); self.history_tree.column('Resultado', width=80, anchor='center')
        self._widget_refs['history_tree_resultado'] = self.history_tree
        self.history_tree.heading('Falhas Freq.', text=f"{t('fastsurance.failures_column')} ↕", command=lambda: self.sort_treeview('Falhas Freq.')); self.history_tree.column('Falhas Freq.', width=120, anchor='w')
        self._widget_refs['history_tree_falhas'] = self.history_tree
        self.history_tree.heading('Data/Hora', text=f"{t('fastsurance.datetime_column')} ↕", command=lambda: self.sort_treeview('Data/Hora')); self.history_tree.column('Data/Hora', width=120, anchor='center')
        self._widget_refs['history_tree_datetime'] = self.history_tree
        
        history_scrollbar = ttk.Scrollbar(self.history_container, orient="vertical", command=self.history_tree.yview); self.history_tree.configure(yscrollcommand=history_scrollbar.set)
        self.history_tree.grid(row=0, column=0, sticky="nsew", padx=5, pady=5); history_scrollbar.grid(row=0, column=1, sticky="ns", pady=5)
        
        self.reports_frame = tk.Frame(self); self.reports_frame.grid(row=0, column=1, sticky="nsew", padx=10, pady=10); self.reports_frame.columnconfigure(0, weight=1); self.reports_frame.rowconfigure(0, weight=0)
        # Larguras fixas para os painéis da direita
        FIXED_RIGHT_PANEL_WIDTH = 620
        
        self.details_container_side = tk.LabelFrame(self.reports_frame, text=t('fastsurance.last_test'), padx=10, pady=10, font=("Helvetica", 10), width=FIXED_RIGHT_PANEL_WIDTH)
        self._widget_refs['details_container_side'] = self.details_container_side
        self.details_container_side.grid(row=0, column=0, sticky="new", pady=(0,5))
        # Mantém largura fixa independentemente do conteúdo
        self.details_container_side.grid_propagate(False)
        
        self.details_table_frame = tk.Frame(self.details_container_side, height=220, width=FIXED_RIGHT_PANEL_WIDTH-20)
        self.details_table_frame.pack(fill="x", expand=False)
        self.details_table_frame.pack_propagate(False)
        self._init_ultimo_teste_table()
        
        # NOVO: Frame para o gráfico de pizza de falhas
        self.pie_chart_container = tk.LabelFrame(self.reports_frame, text=t('fastsurance.failure_distribution'), padx=10, pady=10, font=("Helvetica", 10), width=FIXED_RIGHT_PANEL_WIDTH)
        self._widget_refs['pie_chart_container'] = self.pie_chart_container
        self.pie_chart_container.grid(row=1, column=0, sticky="new", pady=(5,0))
        self.pie_chart_container.grid_propagate(False)
        self._init_pie_chart()
        
        # NOVO: Frame para o gráfico de Pareto de falhas
        self.pareto_chart_container = tk.LabelFrame(self.reports_frame, text=t('fastsurance.pareto_chart'), padx=10, pady=10, font=("Helvetica", 10), width=FIXED_RIGHT_PANEL_WIDTH)
        self._widget_refs['pareto_chart_container'] = self.pareto_chart_container
        self.pareto_chart_container.grid(row=2, column=0, sticky="new", pady=(5,0))
        self.pareto_chart_container.grid_propagate(False)
        self._init_pareto_chart()
        
        self.reports_frame.rowconfigure(3, weight=1); self.empty_space_frame = tk.Frame(self.reports_frame); self.empty_space_frame.grid(row=3, column=0, sticky="nsew")
        
        # CORREÇÃO: Aplica o estado inicial dos botões baseado na licença APÓS todos os widgets serem criados
        self.update_ui_state()

    def perform_safe_shutdown(self):
        fc_log("fastsurance", "Executando desligamento seguro...")
        try:
            ### ALTERADO: usa self.com_port ###
            if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                try:
                    output_buffer = ctypes.create_string_buffer(1)
                    output_len = ctypes.c_uint(0)
                    fc_log("fastsurance", "Desligando Onda Contínua (CW)...")
                    rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x00'), 1, output_buffer, ctypes.byref(output_len))
                finally:
                    ### ALTERADO: usa self.com_port ###
                    rfid_sdk.UHF_RFID_Close(self.com_port)
                    fc_log("fastsurance", "Porta COM fechada.")
        except Exception as e:
            fc_log("fastsurance", f"Erro durante o desligamento seguro: {e}")

    # Função _on_closing removida - popup de confirmação de saída desabilitado
    
    def _on_language_changed(self, old_language=None, new_language=None):
        """Callback quando o idioma é alterado"""
        try:
            self.refresh_language()
        except Exception as e:
            fc_log("fastsurance", f"⚠️ Erro ao atualizar idioma no FastSurance: {e}")
    
    def refresh_language(self):
        """Atualiza todos os textos da interface quando o idioma é alterado"""
        try:
            # Atualiza título apenas em modo standalone (fora do AppShell)
            if (
                hasattr(self, 'root_window')
                and self.app_shell is None
                and self.root_window == self.master
            ):
                self.root_window.title(t('fastsurance.title'))
            
            # Atualiza títulos de frames
            if 'safety_frame' in self._widget_refs:
                self._widget_refs['safety_frame'].config(text=t('fastsurance.safety_system'))
            if 'epc_filter_frame' in self._widget_refs:
                self._widget_refs['epc_filter_frame'].config(text=t('fastsurance.epc_filter'))
            if 'test_type_frame' in self._widget_refs:
                self._widget_refs['test_type_frame'].config(text=t('fastsurance.test_type'))
            if 'input_frame' in self._widget_refs:
                self._widget_refs['input_frame'].config(text=t('fastsurance.test_config'))
            if 'summary_frame' in self._widget_refs:
                self._widget_refs['summary_frame'].config(text=t('fastsurance.execution_summary'))
            if 'graph_frame' in self._widget_refs:
                self._widget_refs['graph_frame'].config(text=t('fastsurance.graph'))
            if 'history_container' in self._widget_refs:
                self._widget_refs['history_container'].config(text=t('fastsurance.test_history'))
            if 'details_container_side' in self._widget_refs:
                self._widget_refs['details_container_side'].config(text=t('fastsurance.last_test'))
            if 'pie_chart_container' in self._widget_refs:
                self._widget_refs['pie_chart_container'].config(text=t('fastsurance.failure_distribution'))
            if 'pareto_chart_container' in self._widget_refs:
                self._widget_refs['pareto_chart_container'].config(text=t('fastsurance.pareto_chart'))
            
            # Atualiza labels
            if 'temperature_label_text' in self._widget_refs:
                self._widget_refs['temperature_label_text'].config(text=t('fastsurance.temperature'))
            if 'vswr_label_text' in self._widget_refs:
                self._widget_refs['vswr_label_text'].config(text=t('fastsurance.vswr'))
            if 'selected_epc_label' in self._widget_refs:
                self._widget_refs['selected_epc_label'].config(text=t('fastsurance.selected_epc'))
            if 'freq_label' in self._widget_refs:
                self._widget_refs['freq_label'].config(text=t('fastsurance.freq_mhz'))
            if 'power_label' in self._widget_refs:
                self._widget_refs['power_label'].config(text=t('fastsurance.threshold_dbm'))
            if 'margin_label' in self._widget_refs:
                self._widget_refs['margin_label'].config(text=t('fastsurance.margin_dbm'))
            if 'attenuator_note' in self._widget_refs:
                self._widget_refs['attenuator_note'].config(text=t('fastsurance.use_attenuator'))
            if 'chip_id_button' in self._widget_refs:
                self._widget_refs['chip_id_button'].config(text=t('fastsurance.chip_id_button'))
            if 'tags_tested_label' in self._widget_refs:
                self._widget_refs['tags_tested_label'].config(text=t('fastsurance.tags_tested'))
            if 'approved_label' in self._widget_refs:
                self._widget_refs['approved_label'].config(text=t('fastsurance.approved'))
            if 'rejected_label' in self._widget_refs:
                self._widget_refs['rejected_label'].config(text=t('fastsurance.rejected'))
            if 'yield_label' in self._widget_refs:
                self._widget_refs['yield_label'].config(text=t('fastsurance.yield'))
            
            # Atualiza labels de teste
            for i in range(5):
                key = f'test_label_{i}'
                if key in self._widget_refs:
                    self._widget_refs[key].config(text=f"{t('fastsurance.test')} {i+1}:")
            
            # Atualiza radio buttons
            if 'radio_gonogo' in self._widget_refs:
                self._widget_refs['radio_gonogo'].config(text=t('fastsurance.gonogo'))
            if 'radio_threshold' in self._widget_refs:
                self._widget_refs['radio_threshold'].config(text=t('fastsurance.threshold'))
            
            # Atualiza botões
            if 'scan_tags_button' in self._widget_refs:
                current = self.scan_tags_button.cget('text')
                if 'Inventariando' not in current and 'Inventorying' not in current:
                    self.scan_tags_button.config(text=t('fastsurance.tag_registration'))
            if 'start_button' in self._widget_refs:
                current = self.start_button.cget('text')
                if 'Verificando' not in current and 'Checking' not in current:
                    test_type = self.test_type_var.get() if hasattr(self, 'test_type_var') else "TAG_TESTS"
                    if test_type == "THRESHOLD_TEST" or test_type == "TAG_TESTS":
                        self.start_button.config(text=t('fastsurance.testing'))
                    else:
                        self.start_button.config(text=t('fastsurance.start_execution'))
            if 'clear_all_button' in self._widget_refs:
                self._widget_refs['clear_all_button'].config(text=t('fastsurance.clear'))
            if 'save_button' in self._widget_refs:
                self._widget_refs['save_button'].config(text=t('fastsurance.save'))
            if 'import_button' in self._widget_refs:
                self._widget_refs['import_button'].config(text=t('fastsurance.import'))
            if 'migrate_button' in self._widget_refs:
                self._widget_refs['migrate_button'].config(text=t('fastsurance.import_from_threshold'))
            if 'threshold_pdf_button' in self._widget_refs:
                self._widget_refs['threshold_pdf_button'].config(text=t('fastsurance.report_pdf'))
            if 'tags_pdf_button' in self._widget_refs:
                self._widget_refs['tags_pdf_button'].config(text=t('fastsurance.report_pdf'))
            
            # Atualiza combobox de EPC
            if hasattr(self, 'epc_combobox'):
                current_val = self.epc_combobox.get()
                if current_val == "Clique em 'Registro de Tag' para carregar" or current_val == "Click on 'Tag Registration' to load":
                    self.epc_combobox.set(t('fastsurance.click_to_load'))
            
            # Atualiza status do EPC se não houver seleção
            if 'epc_status_label' in self._widget_refs:
                current = self.epc_status_label.cget('text')
                if current == "Nenhum EPC selecionado" or current == "No EPC selected":
                    self.epc_status_label.config(text=t('fastsurance.no_epc_selected'))
                elif "EPC selecionado:" in current or "EPC selected:" in current or "selecionado:" in current.lower():
                    if hasattr(self, 'selected_epc') and self.selected_epc:
                        selected_text = t('fastsurance.epc_selected')
                        self.epc_status_label.config(text=f"{selected_text}: {self.selected_epc}", fg="green")
            
            # Recarrega a tabela de histórico para traduzir os status antigos
            if hasattr(self, 'history_tree') and hasattr(self, 'test_history'):
                # Limpa a tabela
                for item in self.history_tree.get_children():
                    self.history_tree.delete(item)
                # Recarrega todos os itens com as traduções atualizadas
                for history_item in self.test_history:
                    self.add_item_to_history_table(history_item)
            
            # Atualiza os cabeçalhos da tabela (mantém símbolos de ordenação se existirem)
            if hasattr(self, 'history_tree'):
                try:
                    # Preserva símbolos de ordenação ao atualizar
                    for col_name, col_key in [('EPC', 'epc_column'), ('Resultado', 'result_column'), 
                                               ('Falhas Freq.', 'failures_column'), ('Data/Hora', 'datetime_column')]:
                        current_text = self.history_tree.heading(col_name)['text']
                        # Verifica se tem símbolo de ordenação
                        if ' ↑' in current_text or ' ↓' in current_text or ' ↕' in current_text:
                            clean_text = current_text.replace(" ↑", "").replace(" ↓", "").replace(" ↕", "")
                            # Se tinha símbolo, preserva; senão adiciona ↕
                            if ' ↕' in current_text:
                                new_text = f"{t(f'fastsurance.{col_key}')} ↕"
                            elif ' ↑' in current_text:
                                new_text = f"{t(f'fastsurance.{col_key}')} ↑"
                            elif ' ↓' in current_text:
                                new_text = f"{t(f'fastsurance.{col_key}')} ↓"
                            else:
                                new_text = f"{t(f'fastsurance.{col_key}')} ↕"
                            self.history_tree.heading(col_name, text=new_text)
                        else:
                            # Se não tinha símbolo, adiciona ↕
                            self.history_tree.heading(col_name, text=f"{t(f'fastsurance.{col_key}')} ↕")
                except Exception as e:
                    fc_log("fastsurance", f"⚠️ Erro ao atualizar cabeçalhos da tabela: {e}")
            
            # Atualiza resumo
            if hasattr(self, 'update_summary_display'):
                self.update_summary_display()
            
            # Atualiza gráficos
            if hasattr(self, 'update_pie_chart'):
                self.update_pie_chart()
            if hasattr(self, 'update_pareto_chart'):
                self.update_pareto_chart()
        except Exception as e:
            fc_log("fastsurance", f"⚠️ Erro ao atualizar idioma no FastSurance: {e}")
            import traceback
            traceback.print_exc()

    def _on_frame_destroy(self, event=None):
        """
        Captura a destruição do frame para garantir salvamento dos dados
        """
        try:
            fc_log("fastsurance", "🔄 FastSurance: Frame sendo destruído, salvando dados...")
            self._save_persistent_data()
        except Exception as e:
            fc_log("fastsurance", f"⚠️ FastSurance: Erro ao salvar dados na destruição: {e}")

    def start_scan_thread(self):
        if self.demo_mode:
            messagebox.showinfo(t('fastsurance.demo_mode_title'), t('fastsurance.demo_mode'))
            return
            
        test_type = self.test_type_var.get()
        
        params = self.get_and_validate_inputs(test_type)
        if not params: return
        self.scan_parameters = params
        
        # Se iniciar Threshold, marca execução e reseta cancelamento
        if test_type == "THRESHOLD_TEST":
            self.threshold_cancel_requested = False
            self.threshold_test_running = True
        
        self.start_button.config(state="disabled", text=t('fastsurance.checking'))
        
        if self.app_shell:
            self.app_shell.set_test_running(True, "FastSurance")
        
        worker_thread = threading.Thread(
            target=self._heating_and_testing_workflow, args=(test_type,), daemon=True
        )
        worker_thread.start()

    def _prime_reader_for_safety_check(self) -> None:
        """Após UHF_RFID_Open: STOP, CW off, potência e freq — GET_PORT_LOSS costuma falhar em leitor 'frio'."""
        try:
            ob = ctypes.create_string_buffer(256)
            ol = ctypes.c_uint(0)
            rfid_sdk.UHF_RFID_Set(RFID_CMD_STOP_INVENTORY, None, 0, ob, ctypes.byref(ol))
            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x00'), 1, ob, ctypes.byref(ol))
            pv = int(HEATING_POWER_DBM * 100)
            pd = bytes([0, 0]) + pv.to_bytes(2, 'big') * 2
            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, ctypes.c_char_p(pd), 6, ob, ctypes.byref(ol))
            fd = b'\x01' + int(915 * 1000).to_bytes(3, 'big')
            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_FREQ_TABLE, ctypes.c_char_p(fd), 4, ob, ctypes.byref(ol))
            time.sleep(0.1)
        except Exception as e:
            fc_log("fastsurance", f"⚠️ FastSurance _prime_reader_for_safety_check: {e}")

    def _safe_stop_reader_hw(self) -> None:
        """
        Para inventário e CW antes de fechar a COM. Fechar com inventário/CW ativo pode
        deixar o leitor em estado inconsistente (perda de comunicação até replugar).
        """
        try:
            ob = ctypes.create_string_buffer(256)
            ol = ctypes.c_uint(0)
            rfid_sdk.UHF_RFID_Set(RFID_CMD_STOP_INVENTORY, None, 0, ob, ctypes.byref(ol))
            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x00'), 1, ob, ctypes.byref(ol))
            time.sleep(0.08)
        except Exception as e:
            fc_log("fastsurance", f"aviso: _safe_stop_reader_hw: {e}")

    def _heating_and_testing_workflow(self, test_type: str):
        port_opened = False
        try:
            if self.app_shell and getattr(self.app_shell, 'com_port', None) is not None:
                self.com_port = self.app_shell.com_port
            if getattr(self, 'port_manager', None):
                self.port_manager.com_port = self.com_port

            if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) != 0:
                self.results_queue.put({'error': f"Falha ao abrir a porta COM{self.com_port}."})
                return

            port_opened = True
            self._prime_reader_for_safety_check()

            reflected_dbm, vswr = self._get_reflected_power_dbm_and_vswr(tx_power_dbm=HEATING_POWER_DBM)
            if reflected_dbm is None or vswr is None:
                self.results_queue.put({'error': t('fastsurance.comm_error_reflected')})
                return

            current_temp = self._get_temperature_worker()
            if current_temp is None:
                self.results_queue.put({'error': t('fastsurance.comm_error_temp')})
                return
            
            self.results_queue.put({'type': 'SAFETY_UPDATE', 'temp': current_temp, 'reflected_power': vswr})

            # PROTEÇÃO DE TEMPERATURA: Bloqueia se >= 50°C
            if current_temp >= TEMPERATURE_LIMIT:
                self.results_queue.put({'error': t('fastsurance.temp_critical').format(temp=f"{current_temp:.1f}")})
                return

            # PROTEÇÃO DE POTÊNCIA REFLETIDA: compara dBm lido no PORT_LOSS (não VSWR)
            if reflected_dbm > REFLECTED_POWER_LIMIT:
                self.results_queue.put({'error': t('fastsurance.reflected_power_high').format(power=f"{reflected_dbm:.1f}")})
                return

            if current_temp < 25.0:
                fc_log("fastsurance", f"🔥 INICIANDO AQUECIMENTO: Temperatura atual: {current_temp}°C")
                cycle_count = 0
                while current_temp < 35.0:
                    # Cancelamento (ex.: botão Limpar no Threshold) durante o aquecimento
                    if test_type == "THRESHOLD_TEST" and getattr(self, 'threshold_cancel_requested', False):
                        fc_log("fastsurance", "Aquecimento interrompido pelo usuario (Limpar / cancelar)")
                        self._safe_stop_reader_hw()
                        self.results_queue.put({'type': 'THRESHOLD_CANCELLED'})
                        return
                    cycle_count += 1
                    fc_log("fastsurance", f"🔥 CICLO AQUECIMENTO #{cycle_count}: Temperatura atual: {current_temp}°C -> Meta: 35°C")
                    self.results_queue.put({'type': 'STATUS_UPDATE', 'text': f'Aquecendo... Ciclo {cycle_count}'})
                    
                    fc_log("fastsurance", f"🔥 EXECUTANDO run_heating_cycle...")
                    self.run_heating_cycle()
                    fc_log("fastsurance", f"🔥 run_heating_cycle CONCLUÍDO")
                    
                    fc_log("fastsurance", f"🔥 MEDINDO TEMPERATURA...")
                    new_temp = self._get_temperature_worker()
                    if new_temp is None:
                        fc_log("fastsurance", f"❌ FALHA ao medir temperatura - PARANDO AQUECIMENTO")
                        return
                    
                    fc_log("fastsurance", f"🔥 TEMPERATURA ANTERIOR: {current_temp}°C")
                    fc_log("fastsurance", f"🔥 TEMPERATURA NOVA: {new_temp}°C")
                    current_temp = new_temp
                    fc_log("fastsurance", f"🔥 TEMPERATURA ATUALIZADA: {current_temp}°C")
                    
                    fc_log("fastsurance", f"🔥 MEDINDO VSWR...")
                    # CORREÇÃO CRUCIAL: Durante aquecimento, VSWR pode interferir com CW
                    # Vamos pular a medição de VSWR durante aquecimento para evitar interferências
                    current_vswr = 1.0  # Valor padrão seguro durante aquecimento
                    fc_log("fastsurance", f"🔥 VSWR DEFINIDO COMO 1.0 (padrão seguro durante aquecimento)")
                    
                    self.results_queue.put({'type': 'SAFETY_UPDATE', 'temp': current_temp, 'reflected_power': current_vswr})
                    
                    # CORREÇÃO: Não verifica VSWR durante aquecimento para evitar paradas
                    # A verificação de VSWR será feita apenas no início e final
                    fc_log("fastsurance", f"🔥 PULANDO VERIFICAÇÃO DE VSWR durante aquecimento")
                    
                    fc_log("fastsurance", f"🔥 VERIFICANDO CONDIÇÃO DO LOOP: {current_temp} < 35.0 = {current_temp < 35.0}")
                    if current_temp >= 35.0:
                        fc_log("fastsurance", f"🔥 CONDIÇÃO DO LOOP: Temperatura {current_temp}°C >= 35°C - SAINDO DO LOOP")
                        break
                    
                    fc_log("fastsurance", f"🔥 AGUARDANDO 0.5s antes do próximo ciclo...")
                    time.sleep(0.5)
                
                fc_log("fastsurance", f"✅ AQUECIMENTO CONCLUÍDO: Temperatura atingiu {current_temp}°C após {cycle_count} ciclos")

            self.results_queue.put({'type': 'STATUS_UPDATE', 'text': 'Executando...'})
            if test_type == "TAG_TESTS":
                self.run_scans_worker()
            elif test_type == "THRESHOLD_TEST":
                self.run_threshold_worker()

        except Exception as e:
            self.results_queue.put({'error': f"Erro inesperado no fluxo de trabalho: {e}"})
            traceback.print_exc()
        finally:
            if port_opened:
                try:
                    self._safe_stop_reader_hw()
                except Exception:
                    pass
                try:
                    rfid_sdk.UHF_RFID_Close(self.com_port)
                except Exception:
                    pass

    def _calculate_vswr_from_reflected_power(self, tx_power_dbm, reflected_power_dbm):
        """Converte potência refletida (dBm) para VSWR"""
        try:
            # Converte dBm para watts
            tx_power_w = 10**(tx_power_dbm / 10) / 1000  # dBm para watts
            reflected_power_w = 10**(reflected_power_dbm / 10) / 1000  # dBm para watts
            
            # Calcula coeficiente de reflexão
            if tx_power_w > 0:
                reflection_coefficient = (reflected_power_w / tx_power_w) ** 0.5
            else:
                reflection_coefficient = 0
            
            # Limita o coeficiente de reflexão entre 0 e 1
            reflection_coefficient = max(0, min(1, reflection_coefficient))
            
            # Calcula VSWR
            if reflection_coefficient < 1:
                vswr = (1 + reflection_coefficient) / (1 - reflection_coefficient)
            else:
                vswr = 1.0  # VSWR mínimo
            
            # Limita VSWR entre 1.0 e 10.0 para valores realistas
            vswr = max(1.0, min(10.0, vswr))
            
            fc_log("fastsurance", f"[DEBUG] FastSurance VSWR: TX={tx_power_dbm:.1f}dBm, Reflected={reflected_power_dbm:.1f}dBm, VSWR={vswr:.2f}")
            return vswr
            
        except Exception as e:
            fc_log("fastsurance", f"[ERRO] FastSurance: Erro no cálculo de VSWR: {e}")
            return 1.0  # VSWR mínimo em caso de erro

    def _get_temperature_worker(self) -> Optional[float]:
        try:
            fc_log("fastsurance", f"🌡️ TEMPERATURE WORKER: Iniciando medição...")
            
            # CORREÇÃO: Usa port_manager centralizado para evitar conflitos
            if hasattr(self, 'port_manager') and self.port_manager:
                def _get_temp_operation():
                    output_buffer = ctypes.create_string_buffer(64)
                    output_len = ctypes.c_uint(0)
                    
                    fc_log("fastsurance", f"🌡️ TEMPERATURE WORKER: Chamando RFID_CMD_GET_TEMPERATURE...")
                    status = rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_TEMPERATURE, None, 0, output_buffer, ctypes.byref(output_len))
                    fc_log("fastsurance", f"🌡️ TEMPERATURE WORKER: Status do comando: {status}")
                    fc_log("fastsurance", f"🌡️ TEMPERATURE WORKER: Tamanho da resposta: {output_len.value}")
                    
                    if status == 0 and output_len.value >= 3:
                        temp_val = struct.unpack('>h', output_buffer.raw[1:3])[0]
                        temp_celsius = temp_val / 100.0
                        fc_log("fastsurance", f"🌡️ TEMPERATURE WORKER: Temperatura lida: {temp_val} -> {temp_celsius}°C")
                        return temp_celsius
                    return None
                
                # Executa usando port_manager centralizado
                result = self.port_manager.execute_with_port(_get_temp_operation, "FastSurance")
                if result is not None:
                    return result
            else:
                # Fallback: abre porta própria se port_manager não estiver disponível
                if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                    try:
                        output_buffer = ctypes.create_string_buffer(64)
                        output_len = ctypes.c_uint(0)
                        
                        status = rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_TEMPERATURE, None, 0, output_buffer, ctypes.byref(output_len))
                        if status == 0 and output_len.value >= 3:
                            temp_val = struct.unpack('>h', output_buffer.raw[1:3])[0]
                            temp_celsius = temp_val / 100.0
                            return temp_celsius
                    finally:
                        rfid_sdk.UHF_RFID_Close(self.com_port)
                
        except Exception as e:
            fc_log("fastsurance", f"❌ TEMPERATURE WORKER: Exceção: {e}")
            self.results_queue.put({'error': f"Exceção em _get_temperature_worker: {e}"})
        
        fc_log("fastsurance", f"❌ TEMPERATURE WORKER: Retornando None")
        return None

    def _get_reflected_power_dbm_and_vswr(self, tx_power_dbm: float) -> Tuple[Optional[float], Optional[float]]:
        """Lê PORT_LOSS: retorna (potência refletida dBm, VSWR derivado). Falha → (None, None)."""
        try:
            if hasattr(self, 'port_manager') and self.port_manager:
                def _read_operation():
                    output_buffer = ctypes.create_string_buffer(64)
                    output_len = ctypes.c_uint(0)
                    status = rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_PORT_LOSS, b'', 0, output_buffer, ctypes.byref(output_len))
                    if status == 0 and output_len.value >= 3:
                        adc_value = struct.unpack('>H', output_buffer.raw[1:3])[0]
                        reflected_power_dbm = -25 + next(
                            (i for i, val in enumerate(RxAdcTable) if adc_value <= val),
                            len(RxAdcTable) - 1,
                        )
                        vswr = self._calculate_vswr_from_reflected_power(tx_power_dbm, reflected_power_dbm)
                        fc_log("fastsurance", f"📊 FastSurance: Potência Refletida={reflected_power_dbm:.1f}dBm, VSWR={vswr:.2f}")
                        return (reflected_power_dbm, vswr)
                    return None

                for attempt in range(4):
                    result = self.port_manager.execute_with_port(_read_operation, "FastSurance")
                    if result is not None:
                        return result
                    if attempt < 3:
                        time.sleep(0.12)
            else:
                if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                    try:
                        output_buffer = ctypes.create_string_buffer(64)
                        output_len = ctypes.c_uint(0)
                        status = rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_PORT_LOSS, b'', 0, output_buffer, ctypes.byref(output_len))
                        if status == 0 and output_len.value >= 3:
                            adc_value = struct.unpack('>H', output_buffer.raw[1:3])[0]
                            reflected_power_dbm = -25 + next(
                                (i for i, val in enumerate(RxAdcTable) if adc_value <= val),
                                len(RxAdcTable) - 1,
                            )
                            vswr = self._calculate_vswr_from_reflected_power(tx_power_dbm, reflected_power_dbm)
                            return (reflected_power_dbm, vswr)
                    finally:
                        rfid_sdk.UHF_RFID_Close(self.com_port)
        except Exception as e:
            fc_log("fastsurance", f"❌ FastSurance: Exceção em _get_reflected_power_dbm_and_vswr: {e}")
            traceback.print_exc()
        return (None, None)

    def _get_vswr_worker(self, tx_power_dbm: float) -> Optional[float]:
        """Compat: retorna apenas VSWR. Para limite de segurança use _get_reflected_power_dbm_and_vswr."""
        _, vswr = self._get_reflected_power_dbm_and_vswr(tx_power_dbm)
        return vswr

    def run_heating_cycle(self):
        """Método de aquecimento COM gerenciamento centralizado de porta COM"""
        try:
            fc_log("fastsurance", f"🔥 HEATING CYCLE: Iniciando configuração...")
            
            # CORREÇÃO: Usa port_manager centralizado para evitar conflitos
            if hasattr(self, 'port_manager') and self.port_manager:
                def _heating_operation():
                    output_buffer = ctypes.create_string_buffer(256)
                    output_len = ctypes.c_uint(0)
                    
                    power_val = int(HEATING_POWER_DBM * 100)
                    power_data = bytes([0,0]) + power_val.to_bytes(2, 'big')*2
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: Configurando potência {HEATING_POWER_DBM}dBm: {power_data.hex()}")
                    power_result = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, ctypes.c_char_p(power_data), 6, output_buffer, ctypes.byref(output_len))
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: Resultado configuração potência: {power_result}")
                    
                    freq_data = b'\x01' + int(915 * 1000).to_bytes(3, 'big')
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: Configurando frequência 915MHz: {freq_data.hex()}")
                    freq_result = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_FREQ_TABLE, ctypes.c_char_p(freq_data), 4, output_buffer, ctypes.byref(output_len))
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: Resultado configuração frequência: {freq_result}")
                    
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: ATIVANDO CW...")
                    cw_on_result = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x01'), 1, output_buffer, ctypes.byref(output_len))
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: Resultado ativação CW: {cw_on_result}")
                    
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: Aguardando 3 segundos com CW ATIVO...")
                    time.sleep(3)
                    
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: DESATIVANDO CW...")
                    cw_off_result = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x00'), 1, output_buffer, ctypes.byref(output_len))
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: Resultado desativação CW: {cw_off_result}")
                    fc_log("fastsurance", f"🔥 HEATING CYCLE: Ciclo concluído")
                    return True
                
                # Executa usando port_manager centralizado
                result = self.port_manager.execute_with_port(_heating_operation, "FastSurance")
                if result:
                    fc_log("fastsurance", f"✅ HEATING CYCLE: Executado com sucesso via port_manager")
                else:
                    fc_log("fastsurance", f"❌ HEATING CYCLE: Falha na execução via port_manager")
                    self.results_queue.put({'error': t('fastsurance.heating_failed')})
            else:
                # Fallback: abre porta própria se port_manager não estiver disponível
                fc_log("fastsurance", f"⚠️ HEATING CYCLE: Port_manager não disponível, usando fallback...")
                if rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
                    try:
                        output_buffer = ctypes.create_string_buffer(256)
                        output_len = ctypes.c_uint(0)
                        
                        power_val = int(HEATING_POWER_DBM * 100)
                        power_data = bytes([0,0]) + power_val.to_bytes(2, 'big')*2
                        power_result = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, ctypes.c_char_p(power_data), 6, output_buffer, ctypes.byref(output_len))
                        
                        freq_data = b'\x01' + int(915 * 1000).to_bytes(3, 'big')
                        freq_result = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_FREQ_TABLE, ctypes.c_char_p(freq_data), 4, output_buffer, ctypes.byref(output_len))
                        
                        cw_on_result = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x01'), 1, output_buffer, ctypes.byref(output_len))
                        time.sleep(3)
                        cw_off_result = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x00'), 1, output_buffer, ctypes.byref(output_len))
                        
                    finally:
                        rfid_sdk.UHF_RFID_Close(self.com_port)
                else:
                    fc_log("fastsurance", f"❌ HEATING CYCLE: Falha ao abrir porta COM{self.com_port} para aquecimento")
                    self.results_queue.put({'error': f"Falha ao abrir porta COM{self.com_port} para aquecimento"})
        
        except Exception as e:
            fc_log("fastsurance", f"❌ HEATING CYCLE: Exceção: {e}")
            self.results_queue.put({'error': f"Exceção em run_heating_cycle: {e}"})

    def _heating_worker(self):
        try:
            output_buffer = ctypes.create_string_buffer(256)  # CORREÇÃO: Buffer maior como no run_heating_cycle
            output_len = ctypes.c_uint(0)
            
            power_val = int(HEATING_POWER_DBM * 100)  # CORREÇÃO: Multiplicar por 100 como no run_heating_cycle
            power_data = bytes([0,0]) + power_val.to_bytes(2, 'big')*2
            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, ctypes.c_char_p(power_data), 6, output_buffer, ctypes.byref(output_len))
            freq_data = b'\x01' + int(915 * 1000).to_bytes(3, 'big')
            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_FREQ_TABLE, ctypes.c_char_p(freq_data), 4, output_buffer, ctypes.byref(output_len))
            
            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x01'), 1, output_buffer, ctypes.byref(output_len))
            time.sleep(3)
            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x00'), 1, output_buffer, ctypes.byref(output_len))
        
        except Exception as e:
            self.results_queue.put({'error': f"Exceção em _heating_worker: {e}"})

    def scan_available_tags(self):
        if self.demo_mode:
            messagebox.showinfo(t('fastsurance.demo_mode_title'), t('fastsurance.demo_mode'))
            return
            
        self.scan_tags_button.config(state="disabled", text=t('fastsurance.scanning'))
        threading.Thread(target=self._scan_tags_worker, daemon=True).start()
    
    def _scan_tags_worker(self):
        ### ALTERADO: usa self.com_port ###
        if self.port_manager.acquire_port("FastSurance", timeout=2.0) and rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE) == 0:
            try:
                output_buffer, output_len = ctypes.create_string_buffer(256), ctypes.c_uint(0)
                power_val_to_send = int(INVENTORY_POWER_DBM * 100)
                power_data = bytes([0x00, 0x00]) + power_val_to_send.to_bytes(2, 'big') * 2
                rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, ctypes.c_char_p(power_data), 6, output_buffer, ctypes.byref(output_len))
                # Frequência de registro de Tag: 917 MHz quando ANATEL estiver ativa, caso contrário 915 MHz
                try:
                    is_licensed = bool(self.license_limits.get('is_licensed', False))
                    license_name = str(self.license_limits.get('license_name', '')).upper()
                    if is_licensed and 'ANATEL' in license_name:
                        reg_freq_mhz = 917.0
                    elif is_licensed and 'ETSI' in license_name:
                        reg_freq_mhz = 866.0
                    else:
                        reg_freq_mhz = 915.0
                except Exception:
                    reg_freq_mhz = 915.0
                freq_data = b'\x01' + int(reg_freq_mhz * 1000).to_bytes(3, 'big')
                rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_FREQ_TABLE, ctypes.c_char_p(freq_data), 4, output_buffer, ctypes.byref(output_len))
                found_tags, max_attempts, consecutive_empty = {}, 20, 0
                for _ in range(max_attempts):
                    inv_tag_input_data = bytes([0x00, 0x64])
                    ret = rfid_sdk.UHF_RFID_Set(RFID_CMD_INV_TAG, ctypes.c_char_p(inv_tag_input_data), 2, output_buffer, ctypes.byref(output_len))
                    if ret == 0 and output_len.value > 5:
                        epc_hex = output_buffer.raw[2:output_len.value - 3].hex().upper()
                        rssi_val = None
                        if rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_RSSIVALU, bytes([0,0]), 2, output_buffer, ctypes.byref(output_len)) == 0:
                            rssi_val = int.from_bytes(output_buffer.raw[1:3], 'big', signed=True) / 10.0
                        if epc_hex not in found_tags or (rssi_val is not None and rssi_val > found_tags[epc_hex]['rssi']):
                            found_tags[epc_hex] = {'rssi': rssi_val if rssi_val is not None else -999}
                        consecutive_empty = 0
                    else:
                        consecutive_empty += 1
                    if consecutive_empty >= 3:
                        break
                    time.sleep(0.1)

                found_tags_list = [{'epc': epc, 'rssi': data['rssi']} for epc, data in found_tags.items()]
                found_tags_list.sort(key=lambda x: x['rssi'], reverse=True)
                self.results_queue.put({'type': 'TAG_SCAN_RESULT', 'tags': found_tags_list})
            except Exception as e:
                self.results_queue.put({'error': f"Erro durante scan de tags: {str(e)}"})
            finally:
                # ALTERADO: usa self.com_port
                rfid_sdk.UHF_RFID_Close(self.com_port)
                self.port_manager.release_port("FastSurance")
        else:
            # ALTERADO: usa self.com_port
            self.results_queue.put({'error': f"Falha ao conectar na COM{self.com_port} para scan de tags."})

    def on_epc_selected(self, event):
        selection = self.epc_combobox.get()
        if selection and "Nenhuma" not in selection:
            self.selected_epc = selection.split(" (RSSI:")[0]
        else:
            self.selected_epc = None
        
        test_type = self.test_type_var.get()
        if self.selected_epc:
            selected_text = t('fastsurance.epc_selected')
            self.epc_status_label.config(text=f"{selected_text}: {self.selected_epc}", fg="green")
            # CORREÇÃO: Só habilita o botão se houver licença
            if test_type == "THRESHOLD_TEST" and self.license_limits.get('is_licensed', False):
                self.start_button.config(state="normal")
        else:
            if test_type == "THRESHOLD_TEST":
                self.epc_status_label.config(text=t('fastsurance.no_epc_selected'), fg="gray")
                self.start_button.config(state="disabled")
            else:
                self.epc_status_label.config(text=t('fastsurance.no_epc_selected'), fg="gray")
        
        # Atualiza o estado dos botões baseado na licença
        self.update_ui_state()
    
    def sync_power_displays(self):
        for i, (power_var, power_display_label) in enumerate(zip(self.power_vars, self.power_display_labels)):
            try: power_display_label.config(text=f"{power_var.get():.1f} dBm")
            except (ValueError, TypeError): power_display_label.config(text="0.0 dBm")

    def _chip_id_test_frequency_mhz(self) -> float:
        """Frequência padrão do utilitário Chip_ID conforme a licença ativa.

        - ETSI: 866 MHz
        - FCC, Anatel, licenças broadband (faixa 800–1000 MHz) e demais: 915 MHz
        """
        try:
            name = str(self.license_limits.get("license_name", "") or "").upper()
            if "ETSI" in name:
                return 866.0
        except Exception:
            pass
        return 915.0

    def _resolve_chip_id_py_path_dev(self) -> Optional[str]:
        """Caminho absoluto de ``Chip_ID.py`` na raiz do projeto (apenas desenvolvimento / subprocess)."""
        dev = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "Chip_ID.py"))
        if os.path.isfile(dev):
            return dev
        return None

    def _launch_chip_id_embedded(self, chip_freq_mhz: float) -> None:
        """Executável PyInstaller: ``ChipIdFinal`` vem do pacote (mesmo runtime que o FastChecker)."""
        try:
            from .chip_id_tool import ChipIdFinal

            top = self.winfo_toplevel()
            self._chip_id_embedded_ref = ChipIdFinal(
                default_freq_mhz=chip_freq_mhz,
                master=top,
            )
            fc_log("fastsurance", f"Chip_ID (embutido) freq_mhz={chip_freq_mhz}")
        except Exception as e:
            fc_log("fastsurance", f"Erro ao abrir Chip_ID embutido: {e}")
            messagebox.showerror(
                t("fastsurance.chip_id_button"),
                t("fastsurance.chip_id_launch_error").format(err=str(e)),
                parent=self.winfo_toplevel(),
            )

    def _launch_chip_id_standalone(self) -> None:
        """Abre o utilitário ``Chip_ID.py`` (leitura TID / identidade do chip) em processo separado."""
        try:
            chip_freq_mhz = self._chip_id_test_frequency_mhz()
            if getattr(sys, "frozen", False):
                self._launch_chip_id_embedded(chip_freq_mhz)
                return

            chip_py = self._resolve_chip_id_py_path_dev()
            if not chip_py:
                messagebox.showerror(
                    t("fastsurance.chip_id_button"),
                    t("fastsurance.chip_id_missing_file").format(
                        path=os.path.abspath(
                            os.path.join(os.path.dirname(__file__), "..", "..", "Chip_ID.py")
                        )
                    ),
                    parent=self.winfo_toplevel(),
                )
                return
            cwd = os.path.dirname(chip_py)
            creationflags = 0
            if sys.platform == "win32":
                creationflags = getattr(subprocess, "DETACHED_PROCESS", 0) | getattr(
                    subprocess, "CREATE_NEW_PROCESS_GROUP", 0
                )
            subprocess.Popen(
                [sys.executable, chip_py, "--freq-mhz", str(chip_freq_mhz)],
                cwd=cwd,
                close_fds=True,
                creationflags=creationflags,
            )
            fc_log(
                "fastsurance",
                f"Chip_ID.py iniciado em processo separado: {chip_py} (freq_mhz={chip_freq_mhz})",
            )
        except Exception as e:
            fc_log("fastsurance", f"Erro ao iniciar Chip_ID.py: {e}")
            messagebox.showerror(
                t("fastsurance.chip_id_button"),
                t("fastsurance.chip_id_launch_error").format(err=str(e)),
                parent=self.winfo_toplevel(),
            )

    def update_ui_for_test_type(self):
        test_type = self.test_type_var.get()
        for entry in self.threshold_power_entries: entry.destroy()
        self.threshold_power_entries.clear()
        
        if test_type == "THRESHOLD_TEST":
            
            # NOVO: Oculta os gráficos no modo de determinação de threshold
            if hasattr(self, 'pie_chart_container'):
                self.pie_chart_container.grid_remove()
            if hasattr(self, 'pareto_chart_container'):
                self.pareto_chart_container.grid_remove()
            
            self.epc_filter_frame.grid(row=1, column=0, sticky="ew", pady=(0,5))
            for entry in self.tag_test_freq_entries: entry.grid_remove()
            for scale in self.power_scales: scale.grid_remove()
            for label in self.power_display_labels: label.grid_remove()
            if hasattr(self, 'safety_margin_entries'):
                for entry in self.safety_margin_entries:
                    entry.grid_remove()
            if hasattr(self, '_widget_refs') and 'margin_label' in self._widget_refs:
                self._widget_refs['margin_label'].grid_remove()
            self.summary_frame.grid_remove(); self.details_container_side.grid_remove(); self.history_container.grid_remove(); self.migrate_button.pack_forget()
            # NOVO: Oculta botão de relatório PDF Tags no modo threshold
            self.tags_pdf_button.pack_forget()
            # NOVO: Mostra botão de relatório PDF Threshold no modo THRESHOLD_TEST
            self.threshold_pdf_button.pack(pady=self.pady_val, fill="x")
            self.power_label.grid(row=0, column=2, padx=5, columnspan=1)
            for i in range(5):
                self.threshold_freq_entries[i].grid(row=i+1, column=1, padx=5)
                power_entry = tk.Entry(self.input_frame, width=10, justify='center', state="disabled", disabledbackground="#f0f0f0", disabledforeground="black")
                power_entry.grid(row=i+1, column=2, padx=10); self.threshold_power_entries.append(power_entry)
                
            # NOVO: Restaura dados de threshold quando o modo é ativado
            self.after(100, self._restore_persistent_interface)
            self.start_button.config(text=t('fastsurance.testing'))
            # CORREÇÃO: Só habilita os botões se houver licença
            if self.license_limits.get('is_licensed', False):
                self.scan_tags_button.config(state="normal", text=t('fastsurance.tag_registration'))
                self.epc_combobox.config(state="readonly")
                if not self.selected_epc:
                    self.epc_status_label.config(text=t('fastsurance.no_epc_selected'), fg="gray")
                    self.start_button.config(state="disabled")
                else:
                    selected_text = t('fastsurance.selected_epc').split(':')[0]
                    self.epc_status_label.config(text=f"{selected_text}: {self.selected_epc}", fg="green")
                    self.start_button.config(state="normal")
            else:
                # Sem licença, botões desabilitados
                self.scan_tags_button.config(state="disabled", text=t('fastsurance.tag_registration'))
                self.epc_combobox.config(state="disabled")
                self.start_button.config(state="disabled")
                self.epc_status_label.config(text=t('fastsurance.license_required'), fg="red")
            if hasattr(self, "chip_id_button"):
                self.chip_id_button.pack(side="right", padx=(8, 4), before=self.attenuator_note)
        else: # TAG_TESTS
            
            if hasattr(self, "chip_id_button"):
                self.chip_id_button.pack_forget()

            # NOVO: Mostra os gráficos no modo de testes de tags
            if hasattr(self, 'pie_chart_container'):
                self.pie_chart_container.grid()
            if hasattr(self, 'pareto_chart_container'):
                self.pareto_chart_container.grid()

            self.epc_filter_frame.grid_remove()
            for entry in self.threshold_freq_entries: entry.grid_remove()
            self.power_label.grid(row=0, column=2, columnspan=2, padx=5)
            if hasattr(self, '_widget_refs') and 'margin_label' in self._widget_refs:
                self._widget_refs['margin_label'].grid(row=0, column=4, padx=(10, 5), sticky="e")
            for i in range(5):
                self.tag_test_freq_entries[i].grid(row=i+1, column=1, padx=5); self.power_scales[i].grid(row=i+1, column=2, padx=10); self.power_display_labels[i].grid(row=i+1, column=3, padx=(0, 10))
                if hasattr(self, 'safety_margin_entries') and i < len(self.safety_margin_entries):
                    self.safety_margin_entries[i].grid(row=i+1, column=4, padx=(10, 5), sticky="w")
            self.sync_power_displays(); self.summary_frame.grid(); self.details_container_side.grid(row=0, column=0, sticky="new", pady=(0,5)); self.history_container.grid(row=4, column=0, sticky="nsew", pady=(5,0));
            # NOVO: Restaura frequências dos testes de tag quando o modo é ativado
            self.after(200, self._restore_tag_test_frequencies)
            self.migrate_button.pack(pady=self.pady_val, fill="x")
            # NOVO: Mostra botão de relatório PDF Tags no modo TAG_TESTS
            self.tags_pdf_button.pack(pady=self.pady_val, fill="x")
            # NOVO: Oculta botão de relatório PDF Threshold no modo TAG_TESTS
            self.threshold_pdf_button.pack_forget()
            # CORREÇÃO: Só habilita o botão se houver licença
            if self.license_limits.get('is_licensed', False):
                self.start_button.config(text="Testar", state="normal")
            else:
                self.start_button.config(text="Testar", state="disabled")
            
        # Atualiza o estado dos botões baseado na licença
        self.update_ui_state()

    def get_and_validate_inputs(self, test_type):
        params = []
        
        min_freq = self.license_limits.get('min_freq', DEFAULT_MIN_FREQ_MHZ)
        max_freq = self.license_limits.get('max_freq', DEFAULT_MAX_FREQ_MHZ)
        min_power = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
        max_power = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
        
        if test_type == "TAG_TESTS":
            for i in range(5):
                freq_str = self.tag_test_freq_entries[i].get()
                if not freq_str: continue
                power_val = self.power_vars[i].get()
                try:
                    freq = float(freq_str)
                    
                    # CORREÇÃO: Validação contra múltiplas faixas da licença
                    freq_valid = False
                    freq_ranges = self.license_limits.get('freq_ranges', [])
                    excluded_ranges = self.license_limits.get('excluded_ranges', [])
                    
                    if freq_ranges:
                        # Valida contra faixas específicas
                        for range_min, range_max in freq_ranges:
                            if range_min <= freq <= range_max:
                                # Verifica se não está em uma faixa excluída
                                in_excluded = False
                                for excl_min, excl_max in excluded_ranges:
                                    if excl_min <= freq <= excl_max:
                                        in_excluded = True
                                        fc_log("fastsurance", f"⚠️ FastSurance: Frequência {freq} MHz está na faixa excluída {excl_min}-{excl_max} MHz")
                                        break
                                
                                if not in_excluded:
                                    freq_valid = True
                                    fc_log("fastsurance", f"✅ FastSurance: Frequência {freq} MHz ACEITA na faixa {range_min}-{range_max} MHz")
                                    break
                                else:
                                    fc_log("fastsurance", f"❌ FastSurance: Frequência {freq} MHz REJEITADA - faixa excluída")
                    else:
                        # Fallback para validação simples
                        freq_valid = (min_freq <= freq <= max_freq)
                    
                    if not freq_valid:
                        ranges_str = " ou ".join([f"{r[0]}-{r[1]} MHz" for r in freq_ranges]) if freq_ranges else f"{min_freq}-{max_freq} MHz"
                        excluded_str = " e ".join([f"{r[0]}-{r[1]} MHz" for r in excluded_ranges]) if excluded_ranges else "nenhuma"
                        messagebox.showerror(t('app.validation_error'), t('fastsurance.validation_error_freq').format(n=i+1, freq=freq, ranges=ranges_str, excluded=excluded_str))
                        return None
                    if not (min_power <= power_val <= max_power):
                        messagebox.showerror(t('app.validation_error'), t('fastsurance.validation_error_power').format(n=i+1, power=power_val, min=min_power, max=max_power)); 
                        return None
                    params.append({'freq': freq, 'power': float(power_val), 'test_idx': i})
                except (ValueError, TypeError): messagebox.showerror(t('app.validation_error'), t('fastsurance.validation_error_freq_invalid').format(n=i+1)); return None
        elif test_type == "THRESHOLD_TEST":
            if not self.selected_epc: messagebox.showwarning(t('fastsurance.no_epc_selected'), t('fastsurance.no_epc_selected_msg')); return None
            for i in range(5):
                freq_str = self.threshold_freq_entries[i].get()
                if not freq_str: continue
                try:
                    freq = float(freq_str)
                    
                    # CORREÇÃO: Validação contra múltiplas faixas da licença (igual aos TAG_TESTS)
                    freq_valid = False
                    freq_ranges = self.license_limits.get('freq_ranges', [])
                    excluded_ranges = self.license_limits.get('excluded_ranges', [])
                    
                    if freq_ranges:
                        # Valida contra faixas específicas
                        for range_min, range_max in freq_ranges:
                            if range_min <= freq <= range_max:
                                # Verifica se não está em uma faixa excluída
                                in_excluded = False
                                for excl_min, excl_max in excluded_ranges:
                                    if excl_min <= freq <= excl_max:
                                        in_excluded = True
                                        fc_log("fastsurance", f"⚠️ FastSurance Threshold: Frequência {freq} MHz está na faixa excluída {excl_min}-{excl_max} MHz")
                                        break
                                
                                if not in_excluded:
                                    freq_valid = True
                                    fc_log("fastsurance", f"✅ FastSurance Threshold: Frequência {freq} MHz ACEITA na faixa {range_min}-{range_max} MHz")
                                    break
                                else:
                                    fc_log("fastsurance", f"❌ FastSurance Threshold: Frequência {freq} MHz REJEITADA - faixa excluída")
                    else:
                        # Fallback para validação simples
                        freq_valid = (min_freq <= freq <= max_freq)
                    
                    if not freq_valid:
                        ranges_str = " ou ".join([f"{r[0]}-{r[1]} MHz" for r in freq_ranges]) if freq_ranges else f"{min_freq}-{max_freq} MHz"
                        excluded_str = " e ".join([f"{r[0]}-{r[1]} MHz" for r in excluded_ranges]) if excluded_ranges else "nenhuma"
                        messagebox.showerror(t('app.validation_error'), t('fastsurance.validation_error_freq_threshold').format(n=i+1, freq=freq, ranges=ranges_str, excluded=excluded_str))
                        return None
                    
                    params.append({'index': i, 'freq': freq})
                except (ValueError, TypeError): messagebox.showerror(t('app.validation_error'), t('fastsurance.validation_error_freq_threshold_invalid').format(n=i+1)); return None
        if not params: messagebox.showwarning(t('app.warning'), t('fastsurance.no_test_config')); return None
        return params

    def run_scans_worker(self):
        results_data = []
        try:
            epc_esperado = None
            max_power = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
            power_data = bytes([0x00, 0x00]) + int(max_power * 100).to_bytes(2, 'big') * 2
            rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, ctypes.c_char_p(power_data), 6, (ctypes.create_string_buffer(1)), ctypes.byref(ctypes.c_uint(0)))
            
            for _ in range(3):
                output_buffer, output_len = ctypes.create_string_buffer(256), ctypes.c_uint(0)
                ret = rfid_sdk.UHF_RFID_Set(RFID_CMD_INV_TAG, bytes([0x00, 0x1E]), 2, output_buffer, ctypes.byref(output_len))
                if ret == 0 and output_len.value > 5:
                    epc_esperado = output_buffer.raw[2:output_len.value - 3].hex().upper()
                    break
                time.sleep(0.05)
            
            if not epc_esperado:
                self.results_queue.put({'error': t('fastsurance.no_reference_tag')})
                return

            for loop_idx, params in enumerate(self.scan_parameters):
                freq, power = params['freq'], params['power']
                output_buffer, output_len = ctypes.create_string_buffer(256), ctypes.c_uint(0)
                test_idx = params.get('test_idx', loop_idx)
                safety_margin = 0.2
                if hasattr(self, 'safety_margin_vars') and 0 <= test_idx < len(self.safety_margin_vars):
                    try:
                        safety_margin = float(self.safety_margin_vars[test_idx].get())
                    except (ValueError, TypeError, tk.TclError):
                        safety_margin = 0.2
                if safety_margin < 0.0 or safety_margin > 5.0:
                    safety_margin = 0.2
                power_with_margin = min(float(power) + safety_margin, max_power)
                power_val_to_send = int(round(power_with_margin * 100))
                power_data = bytes([0x00, 0x00]) + power_val_to_send.to_bytes(2, 'big') * 2
                rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, ctypes.c_char_p(power_data), 6, output_buffer, ctypes.byref(output_len))
                freq_data = b'\x01' + int(freq * 1000).to_bytes(3, 'big')
                rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_FREQ_TABLE, ctypes.c_char_p(freq_data), 4, output_buffer, ctypes.byref(output_len))
                inv_tag_input_data = bytes([0x00, 0x64])
                ret = rfid_sdk.UHF_RFID_Set(RFID_CMD_INV_TAG, ctypes.c_char_p(inv_tag_input_data), 2, output_buffer, ctypes.byref(output_len))
                result_text, rssi_val = "", None
                if ret == 0 and output_len.value > 5:
                    detected_epc = output_buffer.raw[2:output_len.value - 3].hex().upper()
                    if detected_epc == epc_esperado:
                        result_text = detected_epc
                        if rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_RSSIVALU, bytes([0,0]), 2, output_buffer, ctypes.byref(output_len)) == 0:
                            rssi_val = int.from_bytes(output_buffer.raw[1:3], 'big', signed=True) / 10.0
                    else:
                        result_text = "EPC DIFERENTE"
                else:
                    result_text = 'SEM LEITURA'
                results_data.append({
                    'freq': freq,
                    'power': power,
                    'safety_margin': safety_margin,
                    'power_with_margin': round(power_with_margin, 2),
                    'text': result_text,
                    'rssi': rssi_val,
                    'epc_ref': epc_esperado
                })
            
            self.results_queue.put({'type': 'TAG_TESTS', 'data': results_data})
        except Exception as e:
            self.results_queue.put({'error': f"Exceção em run_scans_worker: {e}"})

    def run_threshold_worker(self):
        threshold_results = []
        try:
            # Timeout de segurança total por frequência (em segundos)
            HARD_TIMEOUT_S = 6.0
            for params in self.scan_parameters:
                    # Verificação de cancelamento antes de cada frequência
                    if getattr(self, 'threshold_cancel_requested', False):
                        self._safe_stop_reader_hw()
                        self.results_queue.put({'type': 'THRESHOLD_CANCELLED'})
                        return
                    row_index, freq = params['index'], params['freq']
                    last_successful_power = None
                    
                    output_buffer, output_len = ctypes.create_string_buffer(256), ctypes.c_uint(0)
                    
                    freq_data = b'\x01' + int(freq * 1000).to_bytes(3, 'big')
                    rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_FREQ_TABLE, ctypes.c_char_p(freq_data), 4, output_buffer, ctypes.byref(output_len))

                    start_power_for_run = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
                    min_power = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
                    stop_value = (min_power * 10) - 5

                    start_time = time.time()
                    iteration_count = 0
                    for power_int_x10 in range(start_power_for_run * 10, stop_value, -5):
                        # Verificação de cancelamento a cada iteração de potência
                        if getattr(self, 'threshold_cancel_requested', False):
                            self._safe_stop_reader_hw()
                            self.results_queue.put({'type': 'THRESHOLD_CANCELLED'})
                            return
                        if time.time() - start_time > HARD_TIMEOUT_S:
                            # Evita travamento se não houver leitura
                            fc_log("fastsurance", f"⏱️ Timeout de threshold em {freq} MHz - sem leitura dentro de {HARD_TIMEOUT_S}s")
                            break
                        
                        # PROTEÇÃO DE POTÊNCIA REFLETIDA: Verifica a cada 10 iterações (a cada 5 dBm)
                        iteration_count += 1
                        if iteration_count % 10 == 1:
                            power_check = power_int_x10 / 10.0
                            fc_log("fastsurance", f"🔍 FastSurance (Durante Teste): Verificando potência refletida em {power_check:.1f} dBm...")
                            reflected_dbm, _vswr = self._get_reflected_power_dbm_and_vswr(power_check)
                            if reflected_dbm is not None:
                                fc_log("fastsurance", f"📊 FastSurance (Durante Teste): Potência Refletida = {reflected_dbm:.1f} dBm (Limite: {REFLECTED_POWER_LIMIT} dBm)")
                                if reflected_dbm > REFLECTED_POWER_LIMIT:
                                    error_msg = f"Potência Refletida alta ({reflected_dbm:.1f} dBm) em {power_check:.1f} dBm TX"
                                    fc_log("fastsurance", f"❌ FastSurance: {error_msg}")
                                    self.results_queue.put({'error': error_msg})
                                    break
                                else:
                                    fc_log("fastsurance", f"✅ FastSurance (Durante Teste): Potência Refletida OK")
                        
                        power_dbm = power_int_x10 / 10.0
                        self.results_queue.put({'type': 'LIVE_UPDATE', 'row': row_index, 'power': power_dbm})
                        
                        power_val = int(power_dbm * 100)
                        power_data = bytes([0x00, 0x00]) + power_val.to_bytes(2, 'big') * 2
                        rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, ctypes.c_char_p(power_data), 6, output_buffer, ctypes.byref(output_len))
                        
                        time.sleep(0.02)
                        
                        inv_tag_input_data = bytes([0x00, 0x1E])
                        ret = rfid_sdk.UHF_RFID_Set(RFID_CMD_INV_TAG, ctypes.c_char_p(inv_tag_input_data), 2, output_buffer, ctypes.byref(output_len))
                        
                        if ret == 0 and output_len.value > 5:
                            detected_epc = output_buffer.raw[2:output_len.value - 3].hex().upper()
                            if self.selected_epc and self.selected_epc == detected_epc:
                                last_successful_power = power_dbm
                        else: 
                            break
                    
                    final_threshold_value = None
                    if last_successful_power is None:
                        final_threshold_value = f">{start_power_for_run:.1f}"
                    elif last_successful_power == DEFAULT_MIN_POWER_DBM:
                        final_threshold_value = f"<{DEFAULT_MIN_POWER_DBM:.1f}"
                    else:
                        final_threshold_value = last_successful_power
                    
                    threshold_results.append({'freq': freq, 'threshold': final_threshold_value})

            self.results_queue.put({'type': 'THRESHOLD_TEST', 'data': threshold_results})
        except Exception as e:
            self.results_queue.put({'error': f"Exceção em run_threshold_worker: {e}"})

    def _start_blinking_status(self):
        if self.blinking_job_id: return
        self._toggle_blinking()

    def _stop_blinking_status(self):
        if self.blinking_job_id:
            self.root_window.after_cancel(self.blinking_job_id)
            self.blinking_job_id = None
        
    def _toggle_blinking(self):
        if 'Aquecendo' in self.safety_status_label.cget("text"):
            current_color = self.safety_status_label.cget("foreground")
            next_color = "red" if str(current_color) != "red" else "#A00000"
            self.safety_status_label.config(foreground=next_color)
            self.blinking_job_id = self.root_window.after(500, self._toggle_blinking)
        else:
            self._stop_blinking_status()

    def process_queue(self):
        try:
            result_package = self.results_queue.get_nowait()
            result_type = result_package.get('type')

            self._stop_blinking_status()

            if result_type == 'SAFETY_UPDATE':
                temp = result_package.get('temp')
                reflected_power = result_package.get('reflected_power')
                if temp is not None:
                    self.temperature_label.config(text=f"{temp:.0f}°C")
                if reflected_power is not None:
                    # Agora reflected_power é na verdade VSWR (já convertido)
                    self.vswr_label.config(text=f"{reflected_power:.2f}")
                    if reflected_power > 3.0: self.vswr_status_label.config(text="ALTO", fg="red")
                    elif reflected_power > 2.0: self.vswr_status_label.config(text="ATENÇÃO", fg="orange")
                    else: self.vswr_status_label.config(text="OK", fg="green")
                return
            
            elif result_type == 'STATUS_UPDATE':
                text = result_package['text']
                self.start_button.config(text=text)
                self.safety_status_label.config(text=text)
                if 'Aquecendo' in text:
                    self.safety_status_label.config(fg='red')
                    self._start_blinking_status()
                else:
                    self.safety_status_label.config(fg='orange')
                return

            elif result_type == 'THRESHOLD_CANCELLED':
                # Finaliza estado de execução e restaura UI
                self.threshold_test_running = False
                self.threshold_cancel_requested = False
                if self.app_shell:
                    self.app_shell.set_test_running(False)
                if self.license_limits.get('is_licensed', False):
                    self.start_button.config(state="normal", text="Testar")
                # Limpa gráfico e entradas de potência
                try:
                    self.clear_threshold_graph()
                    for entry in self.threshold_power_entries:
                        entry.config(state="normal"); entry.delete(0, 'end'); entry.config(state="disabled")
                except Exception:
                    pass
                return

            if result_type == 'LIVE_UPDATE':
                row, power = result_package['row'], result_package.get('power')
                if row < len(self.threshold_power_entries):
                    entry = self.threshold_power_entries[row]
                    entry.config(state="normal"); entry.delete(0, 'end')
                    if power is not None: entry.insert(0, f"{power:.1f}")
            
            elif result_type == "THRESHOLD_TEST":
                # Trata resultados com segurança, inclusive casos ">max"/"<min"
                try:
                    self.last_threshold_test_data = result_package['data']
                    for i, result in enumerate(self.last_threshold_test_data):
                        if i < len(self.threshold_power_entries):
                            entry = self.threshold_power_entries[i]
                            entry.config(state="normal")
                            entry.delete(0, 'end')
                            entry.insert(0, str(result.get('threshold', '')))
                            entry.config(state="disabled")
                    
                    # Atualiza o gráfico com dados numéricos apenas
                    self.clear_threshold_graph()  # Limpa dados anteriores
                    min_p = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
                    max_p = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
                    # Verifica se TODOS os resultados são válidos
                    all_valid = True
                    parsed_points = []
                    for result in self.last_threshold_test_data:
                        freq = result.get('freq', 0)
                        threshold_raw = result.get('threshold', None)
                        numeric_threshold = None
                        is_str = isinstance(threshold_raw, str)
                        if isinstance(threshold_raw, (int, float)):
                            numeric_threshold = float(threshold_raw)
                        elif is_str:
                            try:
                                stripped = threshold_raw.replace('>', '').replace('<', '').strip()
                                numeric_threshold = float(stripped)
                            except Exception:
                                numeric_threshold = None
                        # Critério de validade: numérico dentro da faixa e não prefixado com sinal de erro
                        current_valid = (
                            freq > 0 and numeric_threshold is not None and
                            min_p <= numeric_threshold <= max_p and
                            not (is_str and (threshold_raw.startswith('>') or threshold_raw.startswith('<')))
                        )
                        if not current_valid:
                            all_valid = False
                        parsed_points.append((freq, numeric_threshold, current_valid))

                    # Se algum inválido, NÃO plota nada
                    if not all_valid:
                        self.clear_threshold_graph()
                    else:
                        for freq, numeric_threshold, valid in parsed_points:
                            if valid:
                                self.update_threshold_graph(freq, numeric_threshold)
                    
                    # Salva dados persistentemente
                    self._save_persistent_data()
                except Exception as e:
                    # Garante que a UI não trave em caso de erro de parsing
                    fc_log("fastsurance", f"⚠️ FastSurance: erro ao processar resultados de threshold: {e}")
                finally:
                    self.threshold_test_running = False
                    self.threshold_cancel_requested = False
                    if self.app_shell: self.app_shell.set_test_running(False)
                    if self.license_limits.get('is_licensed', False):
                        self.start_button.config(state="normal", text="Testar")
                    else:
                        self.start_button.config(state="disabled", text="Testar")
                    self.safety_status_label.config(text="", fg="green")
                    # Força atualização do estado da UI para garantir consistência
                self.update_ui_state()
            
            elif result_type == "TAG_TESTS":
                self.last_tag_test_data = result_package['data']; result_data = self.last_tag_test_data
                self.all_tag_test_data.append(result_data.copy())
                
                epc_esperado = result_data[0].get('epc_ref') if result_data else "N/A"
                failed_freqs, test_results, all_ok = [], [], True

                for r in result_data:
                    status = "OK" if r.get('text') == epc_esperado else "X"
                    if status != "OK":
                        all_ok = False; failed_freqs.append(str(r.get('freq', '?')))
                    test_results.append({'freq': r.get('freq'), 'epc': r.get('text'), 'status': status})

                if not hasattr(self, 'ultimo_teste_labels') or not self.ultimo_teste_labels: self._init_ultimo_teste_table()
                self.update_ultimo_teste_table(test_results)
                geral_status = t('fastsurance.passed') if all_ok else t('fastsurance.failed')
                tag_epc = epc_esperado if epc_esperado else "N/A"
                
                # NOVO: Verifica se é um teste duplicado ANTES de adicionar contadores
                is_duplicate = self._is_epc_duplicate(tag_epc)
                
                if is_duplicate:
                    fc_log("fastsurance", f"⚠️ FastSurance: EPC {tag_epc} é duplicado - REJEITANDO teste")
                    # Mostra popup de rejeição
                    self._show_duplicate_epc_popup(tag_epc, {'status': 'JÁ TESTADO'})
                    # NÃO adiciona contadores nem ao histórico - EPC duplicado é completamente rejeitado
                else:
                    fc_log("fastsurance", f"✅ FastSurance: EPC {tag_epc} é novo - adicionando ao histórico")
                    # Adiciona contadores apenas se não for duplicado
                    self.tags_testadas += 1
                    passed_text = t('fastsurance.passed')
                    failed_text = t('fastsurance.failed')
                    self.tags_aprovadas += 1 if geral_status == passed_text else 0
                    self.tags_reprovadas += 1 if geral_status == failed_text else 0
                    self.update_summary_display()
                    
                    # Adiciona ao histórico apenas se não for duplicado
                    new_history_item = {'epc': tag_epc, 'status': geral_status, 'failed_freqs': failed_freqs, 'test_params': result_data}
                    if not self.test_history: self.setup_history_header()
                    self.test_history.append(new_history_item)  # Acumula todos os testes
                    self.add_item_to_history_table(new_history_item)
                
                # NOVO: Salva dados persistentemente após cada teste
                self._save_persistent_data()
                
                # NOVO: Atualiza os gráficos imediatamente após cada teste
                if hasattr(self, 'update_pie_chart'):
                    try:
                        self.update_pie_chart()
                        fc_log("fastsurance", f"✅ Gráfico de pizza atualizado após teste")
                    except Exception as e:
                        fc_log("fastsurance", f"⚠️ Erro ao atualizar gráfico de pizza: {e}")
                
                if hasattr(self, 'update_pareto_chart'):
                    try:
                        self.update_pareto_chart()
                        fc_log("fastsurance", f"✅ Gráfico de Pareto atualizado após teste")
                    except Exception as e:
                        fc_log("fastsurance", f"⚠️ Erro ao atualizar gráfico de Pareto: {e}")
                
                if self.app_shell: self.app_shell.set_test_running(False)
                # CORREÇÃO: Habilita o botão para próximo teste se houver licença
                if self.license_limits.get('is_licensed', False):
                    self.start_button.config(state="normal", text="Testar")
                else:
                    self.start_button.config(state="disabled", text="Testar")
                self.safety_status_label.config(text="", fg="green")
                
                # CORREÇÃO: Força atualização do estado da UI para garantir consistência
                self.update_ui_state()
            
            elif result_type == "TAG_SCAN_RESULT":
                found_tags_data = result_package.get('tags', [])
                if found_tags_data:
                    combobox_values = [f"{tag['epc']} (RSSI: {tag['rssi']:.1f} dBm)" if tag['rssi'] != -999 else f"{tag['epc']} (RSSI: N/A)" for tag in found_tags_data]
                    self.epc_combobox['values'] = combobox_values; self.epc_combobox.set(combobox_values[0]);
                    self.on_epc_selected(None)
                else:
                    self.epc_combobox['values'] = [t('fastsurance.no_tags_found')]; self.epc_combobox.set(t('fastsurance.no_tags_found')); self.selected_epc = None
                    self.on_epc_selected(None)
                # CORREÇÃO: Só habilita o botão se houver licença
                if self.license_limits.get('is_licensed', False):
                    self.scan_tags_button.config(state="normal", text=t('fastsurance.tag_registration'))
                else:
                    self.scan_tags_button.config(state="disabled", text=t('fastsurance.tag_registration'))
            
            elif 'error' in result_package:
                self._stop_blinking_status()
                
                if self.app_shell: self.app_shell.set_test_running(False)
                if self.test_type_var.get() == "THRESHOLD_TEST":
                    self.threshold_test_running = False
                    self.threshold_cancel_requested = False
                
                messagebox.showerror(t('fastsurance.error'), result_package['error'])
                current_mode = self.test_type_var.get(); button_text = "Testar" if current_mode == "TAG_TESTS" else "Testar"
                # CORREÇÃO: Só habilita o botão se houver licença
                if self.license_limits.get('is_licensed', False):
                    self.start_button.config(state="normal", text=button_text)
                else:
                    self.start_button.config(state="disabled", text=button_text)
                self.safety_status_label.config(text="Erro!", fg="red")
                if "Falha ao conectar na COM" in result_package.get('error', ''):
                    # CORREÇÃO: Só habilita o botão se houver licença
                    if self.license_limits.get('is_licensed', False):
                        self.scan_tags_button.config(state="normal", text=t('fastsurance.tag_registration'))
                    else:
                        self.scan_tags_button.config(state="disabled", text=t('fastsurance.tag_registration'))
        
        except queue.Empty:
            pass
        except Exception as e:
            fc_log("fastsurance", f"[ERRO] FastSurance process_queue: {e}")
            traceback.print_exc()
        finally:
            try:
                if self.root_window.winfo_exists():
                    self.root_window.after(100, self.process_queue)
            except tk.TclError:
                pass
    
    def update_summary_display(self):
        try:
            self.tags_testadas_var.set(str(self.tags_testadas)); self.tags_aprovadas_var.set(str(self.tags_aprovadas)); self.tags_reprovadas_var.set(str(self.tags_reprovadas))
            yield_percent = (self.tags_aprovadas / self.tags_testadas) * 100 if self.tags_testadas > 0 else 0.0
            self.yield_var.set(f"{yield_percent:.1f}%")
            
            # NOVO: Atualiza os gráficos quando o resumo é atualizado
            if hasattr(self, 'update_pie_chart'):
                try:
                    self.update_pie_chart()
                    fc_log("fastsurance", f"✅ Gráfico de pizza atualizado com resumo")
                except Exception as e:
                    fc_log("fastsurance", f"⚠️ Erro ao atualizar gráfico de pizza com resumo: {e}")
            
            if hasattr(self, 'update_pareto_chart'):
                try:
                    self.update_pareto_chart()
                    fc_log("fastsurance", f"✅ Gráfico de Pareto atualizado com resumo")
                except Exception as e:
                    fc_log("fastsurance", f"⚠️ Erro ao atualizar gráfico de Pareto com resumo: {e}")
        except Exception as e: fc_log("fastsurance", f"Erro ao atualizar resumo: {e}"); traceback.print_exc()
        
    def setup_history_header(self):
        try:
            for item in self.history_tree.get_children(): self.history_tree.delete(item)
        except Exception as e: fc_log("fastsurance", f"Erro ao configurar cabeçalho do histórico: {e}"); traceback.print_exc()

    def add_item_to_history_table(self, history_item):
        try:
            raw_status = history_item.get('status','N/A')
            epc = history_item.get('epc','N/A')
            failed_freq_str = ", ".join(history_item.get('failed_freqs', []))
            
            # Normaliza e traduz o status para exibição
            # Converte valores antigos em português para os valores traduzidos atuais
            passed_text = t('fastsurance.passed')
            failed_text = t('fastsurance.failed')
            
            if raw_status in ['PASSOU', 'Passou', passed_text]:
                status = passed_text
                tags = ('passed',)
            elif raw_status in ['REPROVADO', 'Reprovado', failed_text]:
                status = failed_text
                tags = ('failed',)
            else:
                status = raw_status
                tags = ('failed',)  # Default para failed se não reconhecer
            
            # Usa o datetime do item se disponível, senão usa o atual
            # Formata conforme o idioma selecionado
            if 'datetime' in history_item and history_item['datetime']:
                datetime_raw = history_item['datetime']
                
                # Formata timestamp conforme idioma
                if isinstance(datetime_raw, str) and datetime_raw:
                    try:
                        from datetime import datetime as dt
                        datetime_dt = None
                        
                        # Tenta formato ISO primeiro
                        if 'T' in datetime_raw:
                            datetime_dt = dt.fromisoformat(datetime_raw.replace('Z', '+00:00'))
                        else:
                            # Tenta diferentes formatos comuns
                            formats_to_try = [
                                '%d-%m-%Y %H:%M:%S',   # Formato usado ao salvar
                                '%d/%m/%Y %H:%M:%S',   # Formato com barras e segundos
                                '%d/%m/%Y %H:%M',      # Formato com barras sem segundos
                                '%d-%m-%Y %H:%M',      # Formato com hífens sem segundos
                                '%Y-%m-%d %H:%M:%S',   # Formato ISO sem T
                                '%Y-%m-%d %H:%M'       # Formato ISO sem T e sem segundos
                            ]
                            
                            for fmt in formats_to_try:
                                try:
                                    datetime_dt = dt.strptime(datetime_raw, fmt)
                                    break
                                except ValueError:
                                    continue
                        
                        if datetime_dt:
                            from .i18n import get_translator
                            if get_translator().get_language() == 'en':
                                current_time = datetime_dt.strftime('%m/%d/%y %H:%M:%S')
                            else:
                                current_time = datetime_dt.strftime('%d/%m/%Y %H:%M:%S')
                        else:
                            current_time = datetime_raw
                    except:
                        current_time = datetime_raw
                else:
                    current_time = str(datetime_raw) if datetime_raw else ''
            else:
                # Cria novo timestamp formatado conforme idioma
                from .i18n import get_translator
                if get_translator().get_language() == 'en':
                    current_time = datetime.now().strftime("%m/%d/%y %H:%M:%S")
                else:
                    current_time = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
            
            # Insere o novo item na tabela
            new_item = self.history_tree.insert('', 'end', values=(epc, status, failed_freq_str, current_time), tags=tags)
            
            # NOVO: Scroll automático para mostrar o último teste
            self.history_tree.see(new_item)
            
        except Exception as e: fc_log("fastsurance", f"Erro ao adicionar item ao histórico: {e}"); traceback.print_exc()


    def clear_all_data(self):
        if messagebox.askyesno(t('app.clear'), t('fastsurance.clear_confirm')):
            test_type = self.test_type_var.get()
            if test_type == "TAG_TESTS":
                for entry in self.tag_test_freq_entries: entry.delete(0, 'end')
                max_power = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
                for var in self.power_vars: var.set(max_power)
                self.sync_power_displays()
                
                # Limpa dados de relatório
                self.last_tag_test_data = None
                self.all_tag_test_data.clear()
                self.test_history.clear()
                for item in self.history_tree.get_children():
                    self.history_tree.delete(item)
                self.tags_testadas, self.tags_aprovadas, self.tags_reprovadas = 0, 0, 0
                self.update_summary_display()
                
                # NOVO: Salva dados persistentemente após limpeza completa
                self._save_persistent_data()

            elif test_type == "THRESHOLD_TEST":
                # Interrompe um teste em execução antes de limpar
                if getattr(self, 'threshold_test_running', False):
                    self.threshold_cancel_requested = True
                    self.threshold_test_running = False
                    try:
                        # Atualiza estado global imediatamente
                        if self.app_shell:
                            self.app_shell.set_test_running(False)
                        if self.license_limits.get('is_licensed', False):
                            self.start_button.config(state="normal", text="Testar")
                    except Exception:
                        pass
                for entry in self.threshold_freq_entries: entry.delete(0, 'end')
                for entry in self.threshold_power_entries: entry.config(state="normal"); entry.delete(0, 'end'); entry.config(state="disabled")
                self.last_threshold_test_data = None
                self.selected_epc = None; self.epc_combobox['values'] = []; self.epc_combobox.set("")
                self.on_epc_selected(None)
                # NOVO: Limpa o gráfico de threshold
                self.clear_threshold_graph()

    def save_report_to_file(self):
        test_type = self.test_type_var.get()
        timestamp = datetime.now().strftime('%d.%m.%y_%H.%M.%S')
        if test_type == "TAG_TESTS":
            if not self.test_history:
                messagebox.showwarning(t('app.no_data'), t('fastsurance.no_data_to_save')); return
            report_content = {
                "report_type": "TAG_TESTS",
                "generated_at": timestamp,
                "test_summary_history": self.test_history,
                "safety_margins": [float(var.get()) for var in self.safety_margin_vars] if hasattr(self, 'safety_margin_vars') else [0.2] * 5,
            }
            epc_suffix = self.test_history[-1].get('epc', 'sem_epc') if self.test_history else "sem_epc"
            # Observação: "/" não é permitido em nomes de arquivos no Windows; usamos "Go-NoGo" no lugar
            default_filename = f"FastSurance Go-NoGo {timestamp}.json"
        elif test_type == "THRESHOLD_TEST":
            if not self.last_threshold_test_data:
                messagebox.showwarning(t('app.no_data'), t('fastsurance.no_data_to_save')); return
            report_content = {
                "report_type": "THRESHOLD_TEST",
                "generated_at": timestamp,
                "selected_epc": self.selected_epc,
                "test_data": self.last_threshold_test_data,
                "safety_margins": [float(var.get()) for var in self.safety_margin_vars] if hasattr(self, 'safety_margin_vars') else [0.2] * 5,
            }
            epc_suffix = self.selected_epc or "sem_epc"
            default_filename = f"FastSurance Threshold {timestamp}.json"
        else: return
            
        filepath = filedialog.asksaveasfilename(
            title=t("fastsurance.save_json_title"),
            initialfile=default_filename,
            defaultextension=".json",
            filetypes=[
                (t("export_popup.filetype_json"), "*.json"),
                (t("export_popup.filetype_all"), "*.*"),
            ],
            parent=self,
        )
        if not filepath: return
        try:
            with open(filepath, 'w', encoding='utf-8') as f: json.dump(report_content, f, indent=4)
            messagebox.showinfo(t('app.success'), t('fastsurance.save_success').format(path=filepath))
        except Exception as e: messagebox.showerror(t('app.save_error'), t('fastsurance.save_error').format(error=str(e)))

    def import_report_from_file(self):
        filepath = filedialog.askopenfilename(
            title=t("fastsurance.select_report"),
            defaultextension=".json",
            filetypes=[
                (t("export_popup.filetype_json"), "*.json"),
                (t("export_popup.filetype_all"), "*.*"),
            ],
            parent=self,
        )
        if not filepath: return
        try:
            with open(filepath, 'r', encoding='utf-8') as f: report_data = json.load(f)
        except Exception as e:
            messagebox.showerror(t('app.read_error'), t('fastsurance.read_error').format(error=str(e))); return
        
        report_type = report_data.get("report_type")
        if report_type not in ["TAG_TESTS", "THRESHOLD_TEST"]:
            messagebox.showerror(t('app.invalid_file'), t('fastsurance.invalid_file')); return
        if not messagebox.askyesno(t('app.confirm_import'), t('fastsurance.import_confirm')): return
        
        try:
            self.clear_all_data()
            imported_safety_margins = report_data.get("safety_margins", None)
            if imported_safety_margins is None:
                imported_safety_margins = [0.2] * 5
            if isinstance(imported_safety_margins, list) and hasattr(self, 'safety_margin_vars'):
                for i in range(min(5, len(imported_safety_margins), len(self.safety_margin_vars))):
                    try:
                        margin_val = float(imported_safety_margins[i])
                    except (ValueError, TypeError):
                        margin_val = 0.2
                    if margin_val < 0.0 or margin_val > 5.0:
                        margin_val = 0.2
                    self.safety_margin_vars[i].set(margin_val)
            if report_type == "TAG_TESTS":
                self.test_type_var.set("TAG_TESTS"); self.update_ui_for_test_type()
                self.test_history = report_data.get("test_summary_history", [])
                if self.test_history and 'test_params' in self.test_history[-1]:
                    last_test_params = self.test_history[-1]['test_params']
                    for i, params in enumerate(last_test_params):
                        if i < len(self.tag_test_freq_entries):
                            self.tag_test_freq_entries[i].delete(0, 'end'); self.tag_test_freq_entries[i].insert(0, str(params.get('freq', '')))
                            self.power_vars[i].set(float(params.get('power', DEFAULT_MAX_POWER_DBM)))
                    self.sync_power_displays()
                for item in self.test_history: self.add_item_to_history_table(item)
                self.tags_testadas = len(self.test_history)
                passed_text = t('fastsurance.passed')
                failed_text = t('fastsurance.failed')
                # Normaliza status para comparação (aceita valores antigos e novos)
                self.tags_aprovadas = sum(1 for item in self.test_history 
                                         if item.get('status') in ['PASSOU', 'Passou', passed_text])
                self.tags_reprovadas = self.tags_testadas - self.tags_aprovadas
                self.update_summary_display()
                messagebox.showinfo(t('app.success'), t('fastsurance.import_success_tags'))
            
            elif report_type == "THRESHOLD_TEST":
                self.test_type_var.set("THRESHOLD_TEST"); self.update_ui_for_test_type()
                self.last_threshold_test_data = report_data.get("test_data", [])
                self.selected_epc = report_data.get("selected_epc")
                if self.selected_epc:
                    self.epc_combobox['values'] = [self.selected_epc]; self.epc_combobox.set(self.selected_epc)
                    self.on_epc_selected(None)
                for i, result in enumerate(self.last_threshold_test_data):
                    if i < len(self.threshold_freq_entries):
                        self.threshold_freq_entries[i].delete(0, 'end'); self.threshold_freq_entries[i].insert(0, str(result.get('freq', '')))
                        power_entry = self.threshold_power_entries[i]
                        power_entry.config(state="normal"); power_entry.delete(0, 'end')
                        power_entry.insert(0, str(result.get('threshold', ''))); power_entry.config(state="disabled")
                
                # NOVO: Atualiza o gráfico com os dados importados (apenas se TODOS forem válidos)
                self.clear_threshold_graph()  # Limpa dados anteriores
                if self._validate_threshold_data():
                    min_p = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
                    max_p = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
                    for result in self.last_threshold_test_data:
                        freq = result.get('freq', 0)
                        thr = result.get('threshold')
                        try:
                            val = float(str(thr).replace('>', '').replace('<', '').strip())
                        except Exception:
                            val = None
                        if freq > 0 and val is not None and min_p <= val <= max_p and not (isinstance(thr, str) and (str(thr).startswith('>') or str(thr).startswith('<'))):
                            self.update_threshold_graph(freq, val)
                else:
                    fc_log("fastsurance", "ℹ️ Importação: thresholds inválidos — gráfico não será plotado")
                
                # NOVO: Salva dados persistentemente após importar
                self._save_persistent_data()
                
                messagebox.showinfo(t('app.success'), t('fastsurance.import_success_threshold'))
        except Exception as e:
            messagebox.showerror(t('app.import_error'), t('fastsurance.import_error').format(error=str(e))); traceback.print_exc()

    def _validate_threshold_data(self):
        """
        Valida se os dados do threshold são válidos para importação
        Returns:
            bool: True se válido, False se inválido
        """
        if not self.last_threshold_test_data:
            return False
            
        min_power = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
        max_power = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
        
        for result in self.last_threshold_test_data:
            threshold_val = result.get('threshold')
            
            # Verifica se o threshold é um valor numérico válido
            if isinstance(threshold_val, (int, float)):
                if threshold_val < min_power or threshold_val > max_power:
                    return False
            # Verifica se é uma string indicando valores fora do range
            elif isinstance(threshold_val, str):
                if threshold_val.startswith('<') or threshold_val.startswith('>'):
                    return False
                    
        return True

    def generate_threshold_pdf_report(self):
        """Gera relatório PDF da determinação do threshold"""
        try:
            # Verifica se há dados de threshold
            if not self.last_threshold_test_data:
                messagebox.showwarning(t('app.warning'), t('fastsurance.no_threshold_data'), parent=self)
                return
            
            # Verifica se há tag selecionada
            if not self.selected_epc:
                messagebox.showwarning(t('app.warning'), t('fastsurance.no_tags_selected'), parent=self)
                return
            
            # Diálogo para salvar arquivo
            from tkinter import filedialog
            from datetime import datetime
            
            now = datetime.now()
            filename = f"Relatório_FastSurance Threshold {now.strftime('%d.%m.%y_%H.%M.%S')}.pdf"
            
            filepath = filedialog.asksaveasfilename(
                defaultextension=".pdf",
                filetypes=[(t("fastthreshold.pdf_files"), "*.pdf")],
                initialfile=filename,
                title=t("fastsurance.save_report_title"),
                parent=self,
            )
            
            if not filepath:
                return
            
            fc_log("fastsurance", "Gerando relatório PDF do threshold...")
            self.update()
            
            result = self._generate_threshold_pdf_with_data(filepath, self.last_threshold_test_data)
            
            if result['success']:
                fc_log("fastsurance", "Relatório PDF do threshold gerado com sucesso!")
                messagebox.showinfo(t('app.success'), t('fastsurance.pdf_success').format(path=filepath), parent=self)
                open_path_with_default_app(filepath, log_tag="fastsurance")
            else:
                error_msg = result.get('error', t('fastsurance.unknown_error'))
                fc_log("fastsurance", f"Erro ao gerar relatório PDF: {error_msg}")
                messagebox.showerror(t('app.error'), t('fastsurance.pdf_error').format(error=error_msg), parent=self)
                
        except Exception as e:
            fc_log("fastsurance", f"Erro ao gerar relatório PDF: {str(e)}")
            import traceback
            traceback.print_exc()
            messagebox.showerror(t('fastsurance.error'), t('fastsurance.pdf_generation_error').format(error=str(e)), parent=self)

    def generate_tags_pdf_report(self):
        """Gera relatório PDF dos testes de tags"""
        try:
            # Verifica se há dados de tags testadas
            if not self.test_history:
                messagebox.showwarning(t('app.warning'), t('fastsurance.no_tags_test_data'), parent=self)
                return
            
            # Diálogo para salvar arquivo
            from tkinter import filedialog
            from datetime import datetime
            
            now = datetime.now()
            filename = f"Relatório_FastSurance Tags {now.strftime('%d.%m.%y_%H.%M.%S')}.pdf"
            
            filepath = filedialog.asksaveasfilename(
                defaultextension=".pdf",
                filetypes=[(t("fastthreshold.pdf_files"), "*.pdf")],
                initialfile=filename,
                title=t("fastsurance.save_report_title"),
                parent=self,
            )
            
            if not filepath:
                return
            
            fc_log("fastsurance", "Gerando relatório PDF dos testes de tags...")
            self.update()
            
            result = self._generate_tags_pdf_with_data(filepath, self.test_history)
            
            if result['success']:
                fc_log("fastsurance", "Relatório PDF dos testes de tags gerado com sucesso!")
                messagebox.showinfo(t('fastsurance.success'), t('fastsurance.tags_pdf_success').format(path=filepath), parent=self)
                open_path_with_default_app(filepath, log_tag="fastsurance")
            else:
                error_msg = result.get('error', t('fastsurance.unknown_error'))
                fc_log("fastsurance", f"Erro ao gerar relatório PDF: {error_msg}")
                messagebox.showerror(t('app.error'), t('fastsurance.pdf_error').format(error=error_msg), parent=self)
                
        except Exception as e:
            fc_log("fastsurance", f"Erro ao gerar relatório PDF: {str(e)}")
            import traceback
            traceback.print_exc()
            messagebox.showerror(t('fastsurance.error'), t('fastsurance.pdf_generation_error').format(error=str(e)), parent=self)

    def _generate_threshold_pdf_with_data(self, filepath, threshold_data):
        """Gera PDF usando ReportLab com dados do threshold"""
        try:
            fc_log("fastsurance", f"Gerando PDF do threshold para: {filepath}")
            
            from reportlab.lib.pagesizes import A4
            from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
            from reportlab.lib.units import inch
            from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image, KeepTogether
            from reportlab.lib import colors
            from reportlab.lib.enums import TA_CENTER, TA_LEFT
            import tempfile
            import base64
            from datetime import datetime
            from .pdf_pagination_utils import (
                cond_page_break_before_large_figure,
                cond_page_break_before_footer_block,
            )
            
            fc_log("fastsurance", "Importações do ReportLab concluídas com sucesso")
            
            # Função para obter informações do sistema da licença ativa
            def _get_system_info():
                """Obtém informações do sistema da licença ativa"""
                try:
                    # Tenta obter informações via app_shell (método preferido)
                    if hasattr(self, 'app_shell') and self.app_shell and hasattr(self.app_shell, 'license_manager'):
                        system_info = self.app_shell.license_manager.get_active_license_system_info(self.com_port)
                        # Mantém a versão do software (4.0.0) sem sobrescrever com nome do módulo
                        return system_info
                    
                    # Fallback: cria LicenseManager temporário
                    try:
                        from .license_module import LicenseManager
                        import os
                        LICENSE_DB_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "licenses.json")
                        license_manager = LicenseManager(LICENSE_DB_FILE)
                        system_info = license_manager.get_active_license_system_info(self.com_port)
                        # Mantém a versão do software (4.0.0) sem sobrescrever com nome do módulo
                        return system_info
                    except Exception as fallback_error:
                        fc_log("fastsurance", f"⚠️ Erro no fallback de informações do sistema: {fallback_error}")
                    
                    # Fallback final: informações básicas
                    from .i18n import get_translator
                    translator = get_translator()
                    date_format = '%d/%m/%Y %H:%M:%S' if translator.get_language() == 'pt' else '%m/%d/%y %I:%M:%S %p'
                    return {
                        'software': '4.0.0',
                        'hardware': 'N/A',
                        'firmware': 'N/A',
                        'serial_number': 'N/A',
                        'license': 'N/A',
                        'generated_at': datetime.now().strftime(date_format)
                    }
                    
                except Exception as e:
                    fc_log("fastsurance", f"⚠️ Erro geral ao obter informações do sistema: {e}")
                    from .i18n import get_translator
                    translator = get_translator()
                    date_format = '%d/%m/%Y %H:%M:%S' if translator.get_language() == 'pt' else '%m/%d/%y %I:%M:%S %p'
                    return {
                        'software': '4.0.0',
                        'hardware': 'N/A',
                        'firmware': 'N/A',
                        'serial_number': 'N/A',
                        'license': 'N/A',
                        'generated_at': datetime.now().strftime(date_format)
                    }
            
            # Cria o documento PDF
            fc_log("fastsurance", "Criando documento PDF...")
            doc = SimpleDocTemplate(filepath, pagesize=A4, 
                                  rightMargin=72, leftMargin=72, 
                                  topMargin=72, bottomMargin=18)
            fc_log("fastsurance", "Documento PDF criado com sucesso")
            
            # Estilos
            styles = getSampleStyleSheet()
            title_style = ParagraphStyle(
                'CustomTitle',
                parent=styles['Heading1'],
                fontSize=24,
                spaceAfter=30,
                alignment=TA_CENTER,
                textColor=colors.HexColor('#2c3e50')
            )
            
            heading_style = ParagraphStyle(
                'CustomHeading',
                parent=styles['Heading2'],
                fontSize=16,
                spaceAfter=12,
                textColor=colors.HexColor('#2c3e50')
            )
            
            # Conteúdo do documento
            story = []
            temp_files = []  # Arquivos temporários de imagem para limpar após o build
            
            # Logo Fasttag
            try:
                root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
                logo_path = os.path.join(root, 'assets', 'images', 'fasttag_logo.png')
                if os.path.exists(logo_path):
                    # Mantém proporção circular (1:1) - logo redondo
                    logo_size = 1.5*inch
                    logo_img = Image(logo_path, width=logo_size, height=logo_size)
                    story.append(logo_img)
                    story.append(Spacer(1, 10))
                    fc_log("fastsurance", "Logo Fasttag adicionado ao PDF (proporção circular)")
                else:
                    fc_log("fastsurance", "Logo Fasttag não encontrado")
            except Exception as e:
                fc_log("fastsurance", f"Erro ao adicionar logo: {e}")
            
            # Título
            story.append(Paragraph(t('pdf.report_title_threshold'), title_style))
            story.append(Spacer(1, 20))
            
            # Informações do sistema
            sysinfo = _get_system_info()
            story.append(Paragraph(t('pdf.system_info'), heading_style))
            
            info_text = f"""
            <b>{t('pdf.software')}:</b> {sysinfo['software']}<br/>
            <b>{t('pdf.hardware')}:</b> {sysinfo['hardware']}<br/>
            <b>{t('pdf.firmware')}:</b> {sysinfo['firmware']}<br/>
            <b>{t('pdf.serial_number')}:</b> {sysinfo['serial_number']}<br/>
            <b>{t('pdf.license')}:</b> {sysinfo['license']}<br/>
            <b>{t('pdf.generated_at')}:</b> {sysinfo['generated_at']}
            """
            story.append(Paragraph(info_text, styles['Normal']))
            story.append(Spacer(1, 30))
            
            # Tag selecionada
            story.append(Paragraph(t('fastsurance.selected_epc').split(':')[0] if ':' in t('fastsurance.selected_epc') else t('fastsurance.selected_epc'), heading_style))
            
            # Busca RSSI da tag selecionada
            rssi_value = 'N/A'
            if self.selected_epc and hasattr(self, 'available_tags'):
                # available_tags é uma lista, precisa buscar o RSSI de forma diferente
                for tag_info in self.available_tags:
                    if isinstance(tag_info, dict) and tag_info.get('epc') == self.selected_epc:
                        rssi_value = tag_info.get('rssi', 'N/A')
                        break
                    elif isinstance(tag_info, str) and self.selected_epc in tag_info:
                        # Se for string, extrai RSSI do formato "EPC (RSSI: XX dBm)"
                        if "RSSI:" in tag_info:
                            try:
                                rssi_part = tag_info.split("RSSI:")[1].split("dBm")[0].strip()
                                rssi_value = f"{rssi_part} dBm"
                            except:
                                rssi_value = 'N/A'
                        break
            
            tag_info = f"""
            <b>EPC:</b> {self.selected_epc}<br/>
            <b>RSSI:</b> {rssi_value}
            """
            story.append(Paragraph(tag_info, styles['Normal']))
            story.append(Spacer(1, 30))
            
            # Configuração do teste
            story.append(Paragraph(t('pdf.test_configuration'), heading_style))
            
            # Cabeçalhos da tabela de configuração
            test_label = t('fastsurance.test')
            freq_label = t('fastsurance.frequency_label')
            threshold_label = t('fastsurance.threshold_label')
            margin_label = t('fastsurance.margin_dbm')
            config_headers = [test_label, freq_label, threshold_label, margin_label]
            config_data = [config_headers]
            
            # Dados da configuração
            for i in range(5):
                freq = self.threshold_freq_entries[i].get()
                threshold = self.threshold_power_entries[i].get() if i < len(self.threshold_power_entries) else "N/A"
                margin = 0.2
                if hasattr(self, 'safety_margin_vars') and i < len(self.safety_margin_vars):
                    try:
                        margin = float(self.safety_margin_vars[i].get())
                    except (ValueError, TypeError, tk.TclError):
                        margin = 0.2
                config_data.append([
                    t('fastsurance.test_number').format(n=i+1),
                    freq if freq else "N/A",
                    threshold if threshold else "N/A",
                    f"{margin:.1f}"
                ])
            
            # Cria a tabela de configuração
            config_table = Table(config_data, colWidths=[1.3*inch, 1.8*inch, 1.9*inch, 1.5*inch])
            config_table.setStyle(TableStyle([
                ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#007bff')),
                ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
                ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
                ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
                ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
                ('FONTSIZE', (0, 0), (-1, -1), 10),
                ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
                ('TOPPADDING', (0, 0), (-1, -1), 8),
                ('GRID', (0, 0), (-1, -1), 1, colors.black),
                ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#f8f9fa')])
            ]))
            
            # Mantém a tabela inteira na mesma página
            story.append(KeepTogether(config_table))
            story.append(Spacer(1, 8))
            story.append(Paragraph(t('fastsurance.margin_note_pdf'), styles['Normal']))
            story.append(Spacer(1, 30))
            
            # Gráfico do threshold
            # NOTE: threshold_data é normalmente uma lista de dicts [{'freq': ..., 'threshold': ...}, ...]
            chart_input = {}
            if threshold_data:
                # Normaliza dados para o formato esperado por _generate_threshold_chart_image
                if isinstance(threshold_data, dict):
                    chart_input = threshold_data
                else:
                    freqs = []
                    values = []
                    for item in threshold_data:
                        if not isinstance(item, dict):
                            continue
                        freq = item.get('freq')
                        thr_raw = item.get('threshold')
                        if freq is None:
                            continue
                        # Converte threshold (pode vir como string com '>' ou '<')
                        numeric = None
                        if isinstance(thr_raw, (int, float)):
                            numeric = float(thr_raw)
                        elif isinstance(thr_raw, str):
                            try:
                                stripped = thr_raw.replace('>', '').replace('<', '').strip()
                                numeric = float(stripped)
                            except Exception:
                                numeric = None
                        if numeric is None:
                            continue
                        freqs.append(freq)
                        values.append(numeric)
                    chart_input = {'freq': freqs, 'threshold': values}

            if chart_input.get('freq') and chart_input.get('threshold'):
                story.append(cond_page_break_before_large_figure(4.0))
                story.append(Paragraph(t('pdf.section_chart'), heading_style))
                
                # Gera gráfico do threshold
                chart_image = self._generate_threshold_chart_image(chart_input)
                if chart_image:
                    # Salva a imagem temporariamente
                    with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp_file:
                        tmp_file.write(base64.b64decode(chart_image))
                        tmp_path = tmp_file.name
                        temp_files.append(tmp_path)
                    
                    # Adiciona a imagem ao PDF
                    img = Image(tmp_path, width=7*inch, height=4*inch)
                    
                    story.append(img)
                    story.append(Spacer(1, 16))
            
            # Rodapé do relatório
            story.append(cond_page_break_before_footer_block())
            story.append(Spacer(1, 12))
            story.append(Paragraph(t('pdf.additional_info'), heading_style))
            
            story.append(Paragraph(t('pdf.footer_text'), styles['Normal']))
            
            from datetime import datetime
            from .i18n import get_translator
            translator = get_translator()
            current_lang = translator.get_language()
            if current_lang == 'en':
                timestamp = datetime.now().strftime("%m/%d/%y at %I:%M:%S %p")
            else:
                timestamp = datetime.now().strftime("%d/%m/%Y às %H:%M:%S")
            story.append(Paragraph(f"<b>{t('pdf.document_generated_at')}</b> {timestamp}", styles['Normal']))
            
            # Gera o PDF
            fc_log("fastsurance", "Construindo PDF...")
            doc.build(story)
            fc_log("fastsurance", "PDF construído com sucesso")

            # Limpa arquivos temporários após o build
            for tmp_path in temp_files:
                try:
                    if os.path.exists(tmp_path):
                        os.unlink(tmp_path)
                except Exception:
                    pass
            
            return {'success': True, 'filepath': filepath}
            
        except Exception as e:
            fc_log("fastsurance", f"Erro no _generate_threshold_pdf_with_data: {str(e)}")
            import traceback
            traceback.print_exc()
            return {'success': False, 'error': str(e)}

    def _generate_threshold_chart_image(self, threshold_data):
        """Gera imagem do gráfico de threshold"""
        try:
            import matplotlib.pyplot as plt
            import matplotlib
            matplotlib.use('Agg')  # Backend sem GUI
            import io
            import base64
            
            # Configuração da figura
            plt.figure(figsize=(12, 6))
            plt.style.use('default')
            
            # Dados do gráfico
            frequencies = threshold_data.get('freq', [])
            thresholds = threshold_data.get('threshold', [])
            
            if frequencies and thresholds:
                # Plota o gráfico
                plt.plot(frequencies, thresholds, 'o-', color='#1f77b4', linewidth=2, markersize=6, 
                         markerfacecolor='#1f77b4', markeredgecolor='white', markeredgewidth=1)
                
                # Configurações do gráfico
                plt.title('Threshold vs Frequência - FastSurance', fontsize=14, color='#2c3e50')
                plt.xlabel(t('fastsurance.frequency_label'), fontsize=12)
                plt.ylabel(t('fastsurance.threshold_label'), fontsize=12)
                plt.grid(True, alpha=0.3, linestyle='--')
                
                # Ajusta limites dos eixos
                if frequencies:
                    x_min, x_max = min(frequencies), max(frequencies)
                    x_margin = (x_max - x_min) * 0.1 if x_max > x_min else 1
                    plt.xlim(x_min - x_margin, x_max + x_margin)
                
                if thresholds:
                    y_min, y_max = min(thresholds), max(thresholds)
                    y_margin = (y_max - y_min) * 0.1 if y_max > y_min else 1
                    plt.ylim(y_min - y_margin, y_max + y_margin)
                
                # Configurações de layout
                plt.tight_layout(pad=1.5)
                
                # Salva como string base64
                buffer = io.BytesIO()
                plt.savefig(buffer, format='png', dpi=150, facecolor='white', edgecolor='none', pad_inches=0.2)
                buffer.seek(0)
                
                # Converte para base64
                image_base64 = base64.b64encode(buffer.getvalue()).decode()
                buffer.close()
                plt.close()
                
                return image_base64
            else:
                plt.close()
                return None
                
        except Exception as e:
            fc_log("fastsurance", f"Erro ao gerar gráfico de threshold: {e}")
            return None

    def _generate_tags_pdf_with_data(self, filepath, test_history):
        """Gera PDF usando ReportLab com dados dos testes de tags"""
        try:
            fc_log("fastsurance", f"Gerando PDF dos testes de tags para: {filepath}")
            
            from reportlab.lib.pagesizes import A4
            from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
            from reportlab.lib.units import inch
            from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image, KeepTogether
            from reportlab.lib import colors
            from reportlab.lib.enums import TA_CENTER, TA_LEFT
            import tempfile
            import base64
            from datetime import datetime
            from .pdf_pagination_utils import (
                cond_page_break_before_large_figure,
                cond_page_break_before_medium_figure,
                cond_page_break_before_footer_block,
            )
            
            fc_log("fastsurance", "Importações do ReportLab concluídas com sucesso")
            
            # Função para obter informações do sistema da licença ativa
            def _get_system_info():
                """Obtém informações do sistema da licença ativa"""
                try:
                    # Tenta obter informações via app_shell (método preferido)
                    if hasattr(self, 'app_shell') and self.app_shell and hasattr(self.app_shell, 'license_manager'):
                        system_info = self.app_shell.license_manager.get_active_license_system_info(self.com_port)
                        # Mantém a versão do software (4.0.0) sem sobrescrever com nome do módulo
                        return system_info
                    
                    # Fallback: cria LicenseManager temporário
                    try:
                        from .license_module import LicenseManager
                        import os
                        LICENSE_DB_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "licenses.json")
                        license_manager = LicenseManager(LICENSE_DB_FILE)
                        system_info = license_manager.get_active_license_system_info(self.com_port)
                        # Mantém a versão do software (4.0.0) sem sobrescrever com nome do módulo
                        return system_info
                    except Exception as fallback_error:
                        fc_log("fastsurance", f"⚠️ Erro no fallback de informações do sistema: {fallback_error}")
                    
                    # Fallback final: informações básicas
                    from .i18n import get_translator
                    translator = get_translator()
                    date_format = '%d/%m/%Y %H:%M:%S' if translator.get_language() == 'pt' else '%m/%d/%y %I:%M:%S %p'
                    return {
                        'software': '4.0.0',
                        'hardware': 'N/A',
                        'firmware': 'N/A',
                        'serial_number': 'N/A',
                        'license': 'N/A',
                        'generated_at': datetime.now().strftime(date_format)
                    }
                    
                except Exception as e:
                    fc_log("fastsurance", f"⚠️ Erro geral ao obter informações do sistema: {e}")
                    from .i18n import get_translator
                    translator = get_translator()
                    date_format = '%d/%m/%Y %H:%M:%S' if translator.get_language() == 'pt' else '%m/%d/%y %I:%M:%S %p'
                    return {
                        'software': '4.0.0',
                        'hardware': 'N/A',
                        'firmware': 'N/A',
                        'serial_number': 'N/A',
                        'license': 'N/A',
                        'generated_at': datetime.now().strftime(date_format)
                    }
            
            # Cria o documento PDF
            fc_log("fastsurance", "Criando documento PDF...")
            doc = SimpleDocTemplate(filepath, pagesize=A4, 
                                  rightMargin=72, leftMargin=72, 
                                  topMargin=72, bottomMargin=18)
            fc_log("fastsurance", "Documento PDF criado com sucesso")
            
            # Estilos
            styles = getSampleStyleSheet()
            title_style = ParagraphStyle(
                'CustomTitle',
                parent=styles['Heading1'],
                fontSize=24,
                spaceAfter=30,
                alignment=TA_CENTER,
                textColor=colors.HexColor('#2c3e50')
            )
            
            heading_style = ParagraphStyle(
                'CustomHeading',
                parent=styles['Heading2'],
                fontSize=16,
                spaceAfter=12,
                textColor=colors.HexColor('#2c3e50')
            )
            
            # Conteúdo do documento
            story = []
            
            # Logo Fasttag
            try:
                root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
                logo_path = os.path.join(root, 'assets', 'images', 'fasttag_logo.png')
                if os.path.exists(logo_path):
                    # Mantém proporção circular (1:1) - logo redondo
                    logo_size = 1.5*inch
                    logo_img = Image(logo_path, width=logo_size, height=logo_size)
                    story.append(logo_img)
                    story.append(Spacer(1, 10))
                    fc_log("fastsurance", "Logo Fasttag adicionado ao PDF (proporção circular)")
                else:
                    fc_log("fastsurance", "Logo Fasttag não encontrado")
            except Exception as e:
                fc_log("fastsurance", f"Erro ao adicionar logo: {e}")
            
            # Título
            story.append(Paragraph(t('pdf.report_title_gonogo'), title_style))
            story.append(Spacer(1, 20))
            
            # Informações do sistema
            sysinfo = _get_system_info()
            story.append(Paragraph(t('pdf.system_info'), heading_style))
            
            info_text = f"""
            <b>{t('pdf.software')}:</b> {sysinfo['software']}<br/>
            <b>{t('pdf.hardware')}:</b> {sysinfo['hardware']}<br/>
            <b>{t('pdf.firmware')}:</b> {sysinfo['firmware']}<br/>
            <b>{t('pdf.serial_number')}:</b> {sysinfo['serial_number']}<br/>
            <b>{t('pdf.license')}:</b> {sysinfo['license']}<br/>
            <b>{t('pdf.generated_at')}:</b> {sysinfo['generated_at']}
            """
            story.append(Paragraph(info_text, styles['Normal']))
            story.append(Spacer(1, 30))
            
            # 1. Tabela da configuração do teste
            story.append(Paragraph(t('fastsurance.test_config'), heading_style))
            
            # Cabeçalhos da tabela de configuração
            test_label = t('fastsurance.test')
            freq_label = t('fastsurance.frequency_label')
            power_label = t('fastsurance.threshold_dbm')
            margin_label = t('fastsurance.margin_dbm')
            config_headers = [test_label, freq_label, power_label, margin_label]
            config_data = [config_headers]
            
            # Dados da configuração
            for i in range(5):
                freq = self.tag_test_freq_entries[i].get()
                power = self.power_vars[i].get() if i < len(self.power_vars) else "N/A"
                margin = 0.2
                if hasattr(self, 'safety_margin_vars') and i < len(self.safety_margin_vars):
                    try:
                        margin = float(self.safety_margin_vars[i].get())
                    except (ValueError, TypeError, tk.TclError):
                        margin = 0.2
                config_data.append([
                    t('fastsurance.test_number').format(n=i+1),
                    freq if freq else "N/A",
                    f"{power:.1f}" if isinstance(power, (int, float)) else str(power),
                    f"{margin:.1f}"
                ])
            
            # Cria a tabela de configuração
            config_table = Table(config_data, colWidths=[1.3*inch, 1.8*inch, 1.9*inch, 1.5*inch])
            config_table.setStyle(TableStyle([
                ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#007bff')),
                ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
                ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
                ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
                ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
                ('FONTSIZE', (0, 0), (-1, -1), 10),
                ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
                ('TOPPADDING', (0, 0), (-1, -1), 8),
                ('GRID', (0, 0), (-1, -1), 1, colors.black),
                ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#f8f9fa')])
            ]))
            
            # Mantém a tabela inteira na mesma página
            story.append(KeepTogether(config_table))
            story.append(Spacer(1, 8))
            story.append(Paragraph(t('fastsurance.margin_note_pdf'), styles['Normal']))
            story.append(Spacer(1, 30))
            
            # 2. Resumo da Execução
            # Calcula estatísticas
            total_tags = len(test_history)
            passed_text = t('fastsurance.passed')
            failed_text = t('fastsurance.failed')
            # Normaliza status para comparação (aceita valores antigos e novos)
            approved_tags = sum(1 for tag in test_history 
                               if tag.get('status') in ['PASSOU', 'Passou', passed_text])
            rejected_tags = total_tags - approved_tags
            yield_percent = (approved_tags / total_tags * 100) if total_tags > 0 else 0
            
            # Garante que as traduções estão disponíveis - obtém o tradutor
            from .i18n import get_translator
            translator = get_translator()
            current_lang = translator.get_language()
            
            # Tenta usar as traduções, se não funcionar, usa fallback direto
            metric_label = t('pdf.metric')
            if metric_label == 'pdf.metric':  # Se retornou a própria chave, tradução não existe
                metric_label = 'Métrica' if current_lang == 'pt' else 'Metric'
            
            value_label = t('pdf.value')
            if value_label == 'pdf.value':
                value_label = 'Valor' if current_lang == 'pt' else 'Value'
            
            total_tags_label = t('pdf.total_tags_tested')
            if total_tags_label == 'pdf.total_tags_tested':
                total_tags_label = 'Total de Tags Testadas' if current_lang == 'pt' else 'Total Tags Tested'
            
            tags_approved_label = t('pdf.tags_approved')
            if tags_approved_label == 'pdf.tags_approved':
                tags_approved_label = 'Tags Aprovadas' if current_lang == 'pt' else 'Tags Approved'
            
            tags_rejected_label = t('pdf.tags_rejected')
            if tags_rejected_label == 'pdf.tags_rejected':
                tags_rejected_label = 'Tags Reprovadas' if current_lang == 'pt' else 'Tags Rejected'
            
            summary_data = [
                [metric_label, value_label],
                [total_tags_label, str(total_tags)],
                [tags_approved_label, str(approved_tags)],
                [tags_rejected_label, str(rejected_tags)],
                [t('fastsurance.yield'), f"{yield_percent:.1f}%"]
            ]
            
            summary_table = Table(summary_data, colWidths=[3*inch, 2*inch])
            summary_table.setStyle(TableStyle([
                ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#28a745')),
                ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
                ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
                ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
                ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
                ('FONTSIZE', (0, 0), (-1, -1), 10),
                ('BOTTOMPADDING', (0, 0), (-1, -1), 8),
                ('TOPPADDING', (0, 0), (-1, -1), 8),
                ('GRID', (0, 0), (-1, -1), 1, colors.black),
                ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#f8f9fa')])
            ]))
            
            # Mantém título e tabela juntos na mesma página
            summary_elements = [
                Paragraph(t('fastsurance.execution_summary'), heading_style),
                summary_table,
                Spacer(1, 30)
            ]
            story.append(KeepTogether(summary_elements))
            
            # 3. Tabela do Histórico de Tags Testadas (movida para logo após o resumo)
            story.append(Paragraph(t('fastsurance.test_history'), heading_style))
            
            # Cabeçalhos da tabela de histórico
            test_label = t('fastsurance.test')
            datetime_label = t('fastsurance.datetime_column')
            # Extrai apenas "EPC" do texto completo (remove "da Tag" ou "Tag")
            epc_full = t('fastsurance.epc_column')
            epc_label = 'EPC' if 'EPC' in epc_full else epc_full.split()[0] if epc_full.split() else 'EPC'
            status_label = t('fastsurance.status_column')
            history_headers = [epc_label, status_label, f'{test_label} 1', f'{test_label} 2', f'{test_label} 3', f'{test_label} 4', f'{test_label} 5', datetime_label]
            history_data = [history_headers]
            
            # Dados do histórico (limita a 20 registros para não sobrecarregar o PDF)
            passed_text = t('fastsurance.passed')
            failed_text = t('fastsurance.failed')
            for tag in test_history[:20]:
                epc = tag.get('epc', 'N/A')
                raw_status = tag.get('status', 'N/A')
                # Normaliza status para exibição (aceita valores antigos e novos)
                if raw_status in ['PASSOU', 'Passou', passed_text]:
                    status = passed_text
                elif raw_status in ['REPROVADO', 'Reprovado', failed_text]:
                    status = failed_text
                else:
                    status = raw_status
                failed_freqs = tag.get('failed_freqs', [])
                
                # Converte test_params para resultados dos testes
                test_results = {}
                test_params = tag.get('test_params', [])
                
                # Determina o status de cada teste baseado nos test_params reais
                for i in range(1, 6):
                    test_key = f'test{i}'
                    if i <= len(test_params):
                        test_param = test_params[i-1]
                        # Verifica se o teste falhou (texto é "SEM LEITURA" ou rssi é None)
                        if (test_param.get('text') == 'SEM LEITURA' or 
                            test_param.get('rssi') is None):
                            test_results[test_key] = t('fastsurance.failed')
                        else:
                            test_results[test_key] = t('fastsurance.passed')
                    else:
                        test_results[test_key] = 'N/A'
                
                # Extrai resultados dos testes com cores
                test1 = test_results.get('test1', 'N/A')
                test2 = test_results.get('test2', 'N/A')
                test3 = test_results.get('test3', 'N/A')
                test4 = test_results.get('test4', 'N/A')
                test5 = test_results.get('test5', 'N/A')
                
                # Cria estilo customizado para centralizar
                custom_style = ParagraphStyle(
                    'CustomStyle',
                    parent=styles['Normal'],
                    fontSize=6,
                    alignment=TA_CENTER
                )
                
                # Cria Paragraphs com cores para os status
                def create_status_paragraph(status_text):
                    failed_text = t('fastsurance.failed')
                    if status_text == failed_text:
                        return Paragraph(f'<font color="red">{status_text}</font>', custom_style)
                    else:
                        return status_text
                
                # Usa timestamp atual se não houver - formata conforme idioma
                from .i18n import get_translator
                translator = get_translator()
                if translator.get_language() == 'en':
                    timestamp = datetime.now().strftime('%m/%d/%y %H:%M')
                else:
                    timestamp = datetime.now().strftime('%d/%m/%Y %H:%M')
                
                history_data.append([
                    epc,  # Mostra o EPC completo sem truncamento manual
                    create_status_paragraph(status),
                    create_status_paragraph(str(test1)),
                    create_status_paragraph(str(test2)),
                    create_status_paragraph(str(test3)),
                    create_status_paragraph(str(test4)),
                    create_status_paragraph(str(test5)),
                    timestamp
                ])
            
            # Cria a tabela de histórico (permite repartir entre páginas; cabeçalho repetido)
            history_table = Table(
                history_data,
                colWidths=[2.6*inch, 1.2*inch, 0.7*inch, 0.7*inch, 0.7*inch, 0.7*inch, 0.7*inch, 0.8*inch],
                repeatRows=1,
            )
            history_table.setStyle(TableStyle([
                ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#6c757d')),
                ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
                ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
                ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
                ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
                ('FONTSIZE', (0, 0), (-1, -1), 6),
                ('BOTTOMPADDING', (0, 0), (-1, -1), 6),
                ('TOPPADDING', (0, 0), (-1, -1), 6),
                ('GRID', (0, 0), (-1, -1), 1, colors.black),
                ('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.white, colors.HexColor('#f8f9fa')])
            ]))
            
            story.append(history_table)
            story.append(Spacer(1, 16))
            
            # 4. Gráfico da distribuição de falhas por teste
            story.append(Paragraph(t('fastsurance.failure_distribution'), heading_style))
            
            # Gera gráfico de distribuição de falhas
            failure_chart = self._generate_failure_distribution_chart(test_history)
            temp_files = []  # Lista para armazenar arquivos temporários
            if failure_chart:
                story.append(cond_page_break_before_medium_figure(3.5))
                # Salva a imagem temporariamente
                with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp_file:
                    tmp_file.write(base64.b64decode(failure_chart))
                    tmp_path = tmp_file.name
                    temp_files.append(tmp_path)  # Adiciona à lista para limpeza posterior
                
                # Adiciona a imagem ao PDF (proporção 1:1 para gráfico de pizza)
                img_size = 3.5*inch
                img = Image(tmp_path, width=img_size, height=img_size)
                
                story.append(img)
                story.append(Spacer(1, 16))
            
            # 4. Gráfico de Pareto das Falhas
            # Gera gráfico de Pareto
            pareto_chart = self._generate_pareto_chart(test_history)
            if pareto_chart:
                story.append(cond_page_break_before_large_figure(4.0))
                # Salva a imagem temporariamente
                with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp_file:
                    tmp_file.write(base64.b64decode(pareto_chart))
                    tmp_path = tmp_file.name
                    temp_files.append(tmp_path)  # Adiciona à lista para limpeza posterior
                
                # Adiciona a imagem ao PDF
                img = Image(tmp_path, width=7*inch, height=4*inch)
                
                story.append(Paragraph(t('fastsurance.pareto_chart'), heading_style))
                story.append(img)
                story.append(Spacer(1, 16))
            
            # 5. Rodapé do relatório
            story.append(cond_page_break_before_footer_block())
            story.append(Spacer(1, 12))
            story.append(Paragraph(t('pdf.additional_info'), heading_style))
            
            story.append(Paragraph(t('pdf.footer_text'), styles['Normal']))
            
            from datetime import datetime
            from .i18n import get_translator
            translator = get_translator()
            current_lang = translator.get_language()
            if current_lang == 'en':
                timestamp = datetime.now().strftime("%m/%d/%y at %I:%M:%S %p")
            else:
                timestamp = datetime.now().strftime("%d/%m/%Y às %H:%M:%S")
            story.append(Paragraph(f"<b>{t('pdf.document_generated_at')}</b> {timestamp}", styles['Normal']))
            
            # Gera o PDF
            fc_log("fastsurance", "Construindo PDF...")
            doc.build(story)
            fc_log("fastsurance", "PDF construído com sucesso")
            
            # Limpa arquivos temporários após gerar o PDF
            for temp_file in temp_files:
                try:
                    if os.path.exists(temp_file):
                        os.unlink(temp_file)
                except Exception:
                    pass  # Ignora erros na limpeza
            
            return {'success': True, 'filepath': filepath}
            
        except Exception as e:
            fc_log("fastsurance", f"Erro no _generate_tags_pdf_with_data: {str(e)}")
            import traceback
            traceback.print_exc()
            return {'success': False, 'error': str(e)}

    def _generate_failure_distribution_chart(self, test_history):
        """Gera gráfico de distribuição de falhas por teste"""
        try:
            import matplotlib.pyplot as plt
            import matplotlib
            matplotlib.use('Agg')  # Backend sem GUI
            import io
            import base64
            
            # Configuração da figura (perfeitamente quadrada para gráfico de pizza)
            fig, ax = plt.subplots(figsize=(6, 6), dpi=100)
            plt.style.use('default')
            
            # Remove margens extras
            fig.patch.set_facecolor('white')
            ax.set_facecolor('white')
            
            # Força proporção circular perfeita
            ax.set_aspect('equal', adjustable='box')
            
            # Remove eixos para gráfico de pizza
            ax.set_xticks([])
            ax.set_yticks([])
            ax.spines['top'].set_visible(False)
            ax.spines['right'].set_visible(False)
            ax.spines['bottom'].set_visible(False)
            ax.spines['left'].set_visible(False)
            
            # Remove margens da figura
            fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
            
            # Conta falhas por teste (igual ao módulo)
            test_failures = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
            
            # Para cada entrada no histórico (cada teste de tag)
            for history_item in test_history:
                test_params = history_item.get('test_params', [])
                
                # Verifica cada um dos 5 testes nesta tag
                for i, test_param in enumerate(test_params[:5]):  # Garante máximo de 5 testes
                    test_number = i + 1  # Teste 1, 2, 3, 4, 5
                    
                    # Se o teste falhou (texto é "SEM LEITURA" ou rssi é None)
                    if (test_param.get('text') == 'SEM LEITURA' or 
                        test_param.get('rssi') is None):
                        test_failures[test_number] += 1
            
            
            # Prepara dados para o gráfico de pizza (igual ao módulo)
            labels = [f"Teste {i}" for i in range(1, 6)]
            sizes = [test_failures[i] for i in range(1, 6)]
            colors = ['#ff4444', '#4444ff', '#44ff44', '#ff8844', '#ff44aa']
            
            # Filtra apenas os testes com falhas
            non_zero_data = [(label, size, color) for label, size, color in zip(labels, sizes, colors) if size > 0]
            
            if not non_zero_data:
                # Se não há falhas, cria gráfico vazio
                ax.text(0.5, 0.5, 'Nenhuma falha detectada', ha='center', va='center', transform=ax.transAxes, fontsize=12)
            else:
                # Separa os dados filtrados (igual ao módulo)
                filtered_labels, filtered_sizes, filtered_colors = zip(*non_zero_data)
                
                # Cria gráfico de pizza com configurações exatas do módulo
                wedges, texts, autotexts = ax.pie(filtered_sizes, labels=filtered_labels, colors=filtered_colors, 
                                                 autopct='%1.1f%%', startangle=90, labeldistance=1.1)
                
                # Configura as fatias (igual ao módulo)
                for wedge in wedges:
                    wedge.set_alpha(1.0)
                    wedge.set_edgecolor('black')
                    wedge.set_linewidth(0.8)
                
                # Configura o texto (dobrado para melhor legibilidade)
                for text in texts:
                    text.set_fontsize(24)  # Dobrado de 12 para 24
                    text.set_color('black')
                for autotext in autotexts:
                    autotext.set_fontsize(16)  # Reduzido de 20 para 16
                    autotext.set_color('white')
                    autotext.set_weight('bold')
                
                # Ajusta o layout para evitar sobreposição
                ax.axis('equal')
            
            # Configurações de layout para gráfico circular perfeito
            plt.tight_layout(pad=0.0)
            
            # Salva como string base64 com configurações otimizadas
            buffer = io.BytesIO()
            plt.savefig(buffer, format='png', dpi=150, facecolor='white', edgecolor='none',
                       bbox_inches='tight', pad_inches=0.0, transparent=False)
            buffer.seek(0)
            
            # Converte para base64
            image_base64 = base64.b64encode(buffer.getvalue()).decode()
            buffer.close()
            plt.close()
            
            return image_base64
            
        except Exception as e:
            fc_log("fastsurance", f"Erro ao gerar gráfico de distribuição de falhas: {e}")
            return None

    def _generate_pareto_chart(self, test_history):
        """Gera gráfico de Pareto das falhas"""
        try:
            import matplotlib.pyplot as plt
            import matplotlib
            matplotlib.use('Agg')  # Backend sem GUI
            import io
            import base64
            
            # Configuração da figura
            plt.figure(figsize=(12, 6))
            plt.style.use('default')
            
            # Conta falhas por teste (igual ao módulo)
            test_failures = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
            
            # Para cada entrada no histórico (cada teste de tag)
            for history_item in test_history:
                test_params = history_item.get('test_params', [])
                
                # Verifica cada um dos 5 testes nesta tag
                for i, test_param in enumerate(test_params[:5]):  # Garante máximo de 5 testes
                    test_number = i + 1  # Teste 1, 2, 3, 4, 5
                    
                    # Se o teste falhou (texto é "SEM LEITURA" ou rssi é None)
                    if (test_param.get('text') == 'SEM LEITURA' or 
                        test_param.get('rssi') is None):
                        test_failures[test_number] += 1
            
            
            # Prepara dados para o gráfico de Pareto
            test_numbers = list(range(1, 6))
            failure_counts = [test_failures[i] for i in test_numbers]
            
            # Filtra apenas os testes com falhas
            non_zero_data = [(i, count) for i, count in zip(test_numbers, failure_counts) if count > 0]
            
            if not non_zero_data:
                # Se não há falhas, cria gráfico vazio
                plt.title('Gráfico de Pareto das Falhas - FastSurance', fontsize=14, color='#2c3e50')
                plt.xlabel('Testes', fontsize=12)
                plt.ylabel(t('fastsurance.number_of_failures'), fontsize=12)
                plt.text(0.5, 0.5, 'Nenhuma falha detectada', ha='center', va='center', transform=plt.gca().transAxes, fontsize=12)
                plt.grid(True, alpha=0.3, linestyle='--')
            else:
                # Ordena por número de falhas (descendente)
                sorted_data = sorted(non_zero_data, key=lambda x: x[1], reverse=True)
                test_label = t('fastsurance.test')
                tests = [f'{test_label} {item[0]}' for item in sorted_data]
                failures = [item[1] for item in sorted_data]
                
                # Calcula percentuais acumulados
                total_failures = sum(failures)
                if total_failures > 0:
                    percentages = [f/total_failures*100 for f in failures]
                    cumulative_percentages = []
                    cumulative = 0
                    for p in percentages:
                        cumulative += p
                        cumulative_percentages.append(cumulative)
                else:
                    percentages = [0] * len(failures)
                    cumulative_percentages = [0] * len(failures)
                
                # Cria gráfico de Pareto
                fig, ax1 = plt.subplots()
                
                # Barras de falhas
                bars = ax1.bar(tests, failures, color='#dc3545', alpha=0.7)
                ax1.set_xlabel('Testes', fontsize=12)
                ax1.set_ylabel(t('fastsurance.number_of_failures'), fontsize=12, color='#dc3545')
                ax1.tick_params(axis='y', labelcolor='#dc3545')
                
                # Linha de percentual acumulado
                ax2 = ax1.twinx()
                ax2.plot(tests, cumulative_percentages, color='#007bff', marker='o', linewidth=2, markersize=6)
                ax2.set_ylabel(t('fastsurance.accumulated_percent'), fontsize=12, color='#007bff')
                ax2.tick_params(axis='y', labelcolor='#007bff')
                ax2.set_ylim(0, 100)
                
                # Linha de 80%
                ax2.axhline(y=80, color='red', linestyle='--', alpha=0.7, label='80%')
                
                # Configurações do gráfico
                plt.title('Gráfico de Pareto das Falhas - FastSurance', fontsize=14, color='#2c3e50')
                plt.grid(True, alpha=0.3, linestyle='--')
                
                # Adiciona valores nas barras
                for bar, value in zip(bars, failures):
                    if value > 0:
                        ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.1, 
                                str(value), ha='center', va='bottom', )
            
            # Configurações de layout
            plt.tight_layout(pad=1.5)
            
            # Salva como string base64
            buffer = io.BytesIO()
            plt.savefig(buffer, format='png', dpi=150, facecolor='white', edgecolor='none', pad_inches=0.2)
            buffer.seek(0)
            
            # Converte para base64
            image_base64 = base64.b64encode(buffer.getvalue()).decode()
            buffer.close()
            plt.close()
            
            return image_base64
            
        except Exception as e:
            fc_log("fastsurance", f"Erro ao gerar gráfico de Pareto: {e}")
            return None

    def migrate_threshold_data(self):
        if not self.last_threshold_test_data: 
            messagebox.showwarning(t('app.migrate_data'), t('fastsurance.no_threshold_to_migrate')); 
            return
            
        # NOVO: Valida se o threshold foi determinado corretamente
        if not self._validate_threshold_data():
            min_power = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
            max_power = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
            messagebox.showerror(t('app.invalid_threshold'), 
                t('fastsurance.invalid_threshold').format(min=min_power, max=max_power))
            return
            
        if not messagebox.askyesno(t('app.migrate_data'), t('fastsurance.migrate_confirm')): 
            return
            
        for i, result in enumerate(self.last_threshold_test_data):
            if i >= len(self.tag_test_freq_entries): break
            freq, power_val = result['freq'], result['threshold']
            self.tag_test_freq_entries[i].delete(0, 'end'); self.tag_test_freq_entries[i].insert(0, str(freq))
            if isinstance(power_val, (int, float)):
                max_power = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
                min_power = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
                clamped_power = max(min(power_val, max_power), min_power)
                self.power_vars[i].set(clamped_power)
            elif isinstance(power_val, str):
                max_power = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
                min_power = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
                if ">" in power_val: self.power_vars[i].set(max_power)
                elif "<" in power_val: self.power_vars[i].set(min_power)
        self.sync_power_displays()
        messagebox.showinfo(t('app.success'), t('fastsurance.migrate_success'))

    def _init_ultimo_teste_table(self):
        if not hasattr(self, 'details_table_frame') or not self.details_table_frame.winfo_exists(): return
        for widget in self.details_table_frame.winfo_children(): widget.destroy()
        headers = ["F (MHz)", "EPC", "Status"]
        for col, text in enumerate(headers): tk.Label(self.details_table_frame, text=text, font=("Helvetica", 10)).grid(row=0, column=col, padx=10, pady=2, sticky="nw")
        self.ultimo_teste_labels = []
        for i in range(5):
            row_labels = []
            for col in range(3):
                lbl = tk.Label(self.details_table_frame, text="", font=("Helvetica", 9)); lbl.grid(row=i+1, column=col, padx=10, pady=1, sticky="nw"); row_labels.append(lbl)
            self.ultimo_teste_labels.append(row_labels)

    def update_ultimo_teste_table(self, test_results):
        try:
            if not hasattr(self, 'details_table_frame') or not self.details_table_frame.winfo_exists(): return
            for i, result in enumerate(test_results):
                if i >= len(self.ultimo_teste_labels): break
                freq, epc, status = result.get('freq',"---"), result.get('epc',"---"), result.get('status',"---")
                self.ultimo_teste_labels[i][0].config(text=f"{freq}")
                self.ultimo_teste_labels[i][1].config(text=f"{epc}")
                self.ultimo_teste_labels[i][2].config(text=f"{status}")
            for j in range(len(test_results), len(self.ultimo_teste_labels)):
                for lbl in self.ultimo_teste_labels[j]: lbl.config(text="")
        except Exception as e: fc_log("fastsurance", f"Erro ao atualizar tabela último teste: {e}"); traceback.print_exc()

    def sort_treeview(self, column):
        """
        Ordena a tabela de histórico pela coluna especificada
        """
        try:
            # Obtém todos os itens da tabela com seus valores
            items = []
            for item in self.history_tree.get_children(''):
                values = self.history_tree.item(item, 'values')
                items.append((values, item))
            
            # Verifica se é a mesma coluna para alternar direção
            if self.current_sort_column == column:
                self.current_sort_reverse = not self.current_sort_reverse
            else:
                self.current_sort_column = column
                self.current_sort_reverse = False
            
            # Determina o tipo de ordenação baseado na coluna
            if column == "Data/Hora":
                # Ordenação cronológica (DD/MM/YYYY HH:MM)
                items.sort(key=lambda x: datetime.strptime(x[0][3], "%d/%m/%Y %H:%M") if x[0][3] != 'N/A' else datetime.min, reverse=self.current_sort_reverse)
            elif column == "Resultado":
                # Ordenação hierárquica por resultado
                passed_text = t('fastsurance.passed')
                failed_text = t('fastsurance.failed')
                def result_key(x):
                    status = x[0][1]
                    # Normaliza status para comparação (aceita valores antigos e novos)
                    if status in ['PASSOU', 'Passou', passed_text]:
                        return 1
                    elif status in ['REPROVADO', 'Reprovado', failed_text]:
                        return 2
                    return 0
                items.sort(key=result_key, reverse=self.current_sort_reverse)
            else:
                # Ordenação alfabética para outras colunas
                items.sort(key=lambda x: x[0][self._get_column_index(column)], reverse=self.current_sort_reverse)
            
            # Reinsere os itens na ordem correta
            for item in self.history_tree.get_children(''):
                self.history_tree.delete(item)
            for values, _ in items:
                self.history_tree.insert('', 'end', values=values)
            
            self.update_sort_indicator(column)
        except Exception as e:
            fc_log("fastsurance", f"Erro ao ordenar Treeview: {e}")
            # Em caso de erro, recarrega a tabela
            self._reload_history_table()

    def update_sort_indicator(self, column):
        """
        Atualiza os indicadores de ordenação nas colunas
        """
        try:
            # Remove símbolos de todas as colunas
            for col in ["EPC", "Resultado", "Falhas Freq.", "Data/Hora"]:
                current_text = self.history_tree.heading(col)['text']
                # Remove símbolos existentes
                clean_text = current_text.replace(" ↑", "").replace(" ↓", "").replace(" ↕", "")
                self.history_tree.heading(col, text=f"{clean_text} ↕") # Adiciona ↕ de volta para indicar que é ordenável
            
            # Adiciona o símbolo de ordenação para a coluna atual
            current_text = self.history_tree.heading(column)['text']
            clean_text = current_text.replace(" ↑", "").replace(" ↓", "").replace(" ↕", "")
            
            if self.current_sort_reverse:
                self.history_tree.heading(column, text=f"{clean_text} ↓")
            else:
                self.history_tree.heading(column, text=f"{clean_text} ↑")
        except Exception as e:
            fc_log("fastsurance", f"Erro ao atualizar indicador de ordenação: {e}")

    def _reload_history_table(self):
        """
        Recarrega a tabela de histórico em caso de erro na ordenação
        """
        try:
            # Limpa a tabela atual
            for item in self.history_tree.get_children(''):
                self.history_tree.delete(item)
            
            # Reinsere todos os itens do histórico
            for history_item in self.test_history:
                self.add_item_to_history_table(history_item)
        except Exception as e:
            fc_log("fastsurance", f"Erro ao recarregar tabela de histórico: {e}")

    def _get_column_index(self, column):
        """
        Retorna o índice da coluna na lista de valores
        """
        columns = ["EPC", "Resultado", "Falhas Freq.", "Data/Hora"]
        try:
            return columns.index(column)
        except ValueError:
            return 0

    def _init_pie_chart(self):
        """
        Inicializa o gráfico de pizza de falhas por teste
        """
        try:
            # Cria o frame para o gráfico com largura fixa
            FIXED_RIGHT_PANEL_WIDTH = 620
            self.pie_chart_frame = tk.Frame(self.pie_chart_container, height=300, width=FIXED_RIGHT_PANEL_WIDTH-20)
            self.pie_chart_frame.pack(fill="x", expand=False)
            self.pie_chart_frame.pack_propagate(False)
            
            # Cria a figura do matplotlib
            self.pie_figure = Figure(figsize=(6, 4), dpi=100, facecolor='none'); self.pie_figure.patch.set_alpha(0.0)
            self.pie_ax = self.pie_figure.add_subplot(111, facecolor='none'); self.pie_ax.patch.set_alpha(0.0)
            
            # Cria o canvas do Tkinter
            self.pie_canvas = FigureCanvasTkAgg(self.pie_figure, self.pie_chart_frame)
            canvas_widget = self.pie_canvas.get_tk_widget()
            canvas_widget.configure(bg=self.pie_chart_container.cget("bg"), highlightthickness=0, bd=0)
            canvas_widget.pack(fill="both", expand=True)
            
            # Inicializa com dados vazios
            self.update_pie_chart()
            
            # Não exibir título (informação redundante)
            self.pie_ax.set_title('')
            
        except Exception as e:
            fc_log("fastsurance", f"Erro ao inicializar gráfico de pizza: {e}")
            traceback.print_exc()

    def update_pie_chart(self):
        """
        Atualiza o gráfico de pizza com dados dos últimos testes
        Mostra falhas por teste (1 a 5) em vez de por frequência
        """
        try:
            # Limpa o gráfico anterior
            self.pie_ax.clear()
            self.pie_ax.set_facecolor('none')
            self.pie_ax.patch.set_alpha(0.0)
            self.pie_figure.patch.set_alpha(0.0)
            
            # Conta falhas por posição do teste (1 a 5)
            test_failures = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
            
            # Para cada entrada no histórico (cada teste de tag)
            for history_item in self.test_history:
                test_params = history_item.get('test_params', [])
                
                # Verifica cada um dos 5 testes nesta tag
                for i, test_param in enumerate(test_params[:5]):  # Garante máximo de 5 testes
                    test_number = i + 1  # Teste 1, 2, 3, 4, 5
                    
                    # Se o teste falhou (texto é "SEM LEITURA" ou rssi é None)
                    if (test_param.get('text') == 'SEM LEITURA' or 
                        test_param.get('rssi') is None):
                        test_failures[test_number] += 1
            
            # Prepara dados para o gráfico
            test_label = t('fastsurance.test')
            labels = [f"{test_label} {i}" for i in range(1, 6)]
            sizes = [test_failures[i] for i in range(1, 6)]
            colors = ['#ff4444', '#4444ff', '#44ff44', '#ff8844', '#ff44aa']
            
            # Filtra apenas os testes com falhas
            non_zero_data = [(label, size) for label, size in zip(labels, sizes) if size > 0]
            
            if non_zero_data:
                non_zero_labels, non_zero_sizes = zip(*non_zero_data)
                non_zero_colors = colors[:len(non_zero_labels)]
                
                wedges, texts, autotexts = self.pie_ax.pie(non_zero_sizes, labels=non_zero_labels, 
                                                         colors=non_zero_colors, autopct='%1.1f%%',
                                                         startangle=90, labeldistance=1.1)
                
                # Configura as fatias
                for wedge in wedges:
                    wedge.set_alpha(1.0)
                    wedge.set_edgecolor('black')
                    wedge.set_linewidth(0.8)
                
                # Configura o texto
                for text in texts:
                    text.set_fontsize(9)
                    text.set_color('black')
                for autotext in autotexts:
                    autotext.set_fontsize(8)
                    autotext.set_color('white')
                    autotext.set_weight('bold')
            else:
                # Se não há falhas, deixa o gráfico completamente vazio
                self.pie_ax.clear()
                self.pie_ax.set_facecolor('white')
                self.pie_ax.set_xticks([])
                self.pie_ax.set_yticks([])
                self.pie_ax.axis('off')
                self.pie_ax.set_title('')
                self.pie_ax.set_xlabel('')
                self.pie_ax.set_ylabel('')
            
            # Configurações do gráfico
            self.pie_ax.axis('equal')
            self.pie_canvas.draw()
            
        except Exception as e:
            fc_log("fastsurance", f"Erro ao atualizar gráfico de pizza: {e}")
            traceback.print_exc()
    
    def _init_pareto_chart(self):
        """
        Inicializa o gráfico de Pareto de falhas por teste
        """
        try:
            # Cria o frame para o gráfico com largura fixa
            FIXED_RIGHT_PANEL_WIDTH = 620
            self.pareto_chart_frame = tk.Frame(self.pareto_chart_container, height=300, width=FIXED_RIGHT_PANEL_WIDTH-20)
            self.pareto_chart_frame.pack(fill="x", expand=False)
            self.pareto_chart_frame.pack_propagate(False)
            
            # Cria a figura do matplotlib
            self.pareto_figure = Figure(figsize=(6, 4), dpi=100, facecolor='none')
            self.pareto_figure.patch.set_alpha(0.0)
            self.pareto_ax = self.pareto_figure.add_subplot(111, facecolor='none')
            self.pareto_ax.patch.set_alpha(0.0)
            
            # Configurações iniciais para evitar cortes
            self.pareto_figure.tight_layout(pad=2.0)
            
            # Cria o canvas do Tkinter
            self.pareto_canvas = FigureCanvasTkAgg(self.pareto_figure, self.pareto_chart_frame)
            canvas_widget = self.pareto_canvas.get_tk_widget()
            canvas_widget.configure(bg=self.pareto_chart_container.cget("bg"), highlightthickness=0, bd=0)
            canvas_widget.pack(fill="both", expand=True)
            
            # Inicializa com dados vazios
            self.update_pareto_chart()
            
            # Não exibir título (informação redundante)
            self.pareto_ax.set_title('')
            
        except Exception as e:
            fc_log("fastsurance", f"Erro ao inicializar gráfico de Pareto: {e}")
            traceback.print_exc()
    
    def update_pareto_chart(self):
        """
        Atualiza o gráfico de Pareto com dados dos últimos testes
        Mostra falhas por teste ordenadas por frequência (Pareto)
        """
        try:
            # Limpa completamente o gráfico anterior
            self.pareto_ax.clear()
            self.pareto_ax.set_facecolor('none')
            self.pareto_ax.patch.set_alpha(0.0)
            self.pareto_figure.patch.set_alpha(0.0)
            
            # Remove todos os eixos secundários existentes
            for ax in self.pareto_figure.get_axes():
                if ax != self.pareto_ax:
                    ax.remove()
            
            # Conta falhas por posição do teste (1 a 5)
            test_failures = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
            
            # Para cada entrada no histórico (cada teste de tag)
            for history_item in self.test_history:
                test_params = history_item.get('test_params', [])
                
                # Verifica cada um dos 5 testes nesta tag
                for i, test_param in enumerate(test_params[:5]):  # Garante máximo de 5 testes
                    test_number = i + 1  # Teste 1, 2, 3, 4, 5
                    
                    # Se o teste falhou (texto é "SEM LEITURA" ou rssi é None)
                    if (test_param.get('text') == 'SEM LEITURA' or 
                        test_param.get('rssi') is None):
                        test_failures[test_number] += 1
            
            
            # Prepara dados para o gráfico de Pareto
            test_numbers = list(range(1, 6))
            failure_counts = [test_failures[i] for i in test_numbers]
            
            # Filtra apenas os testes com falhas
            non_zero_data = [(test_num, count) for test_num, count in zip(test_numbers, failure_counts) if count > 0]
            
            if non_zero_data:
                # Ordena por número de falhas (decrescente) para Pareto
                non_zero_data.sort(key=lambda x: x[1], reverse=True)
                sorted_tests, sorted_counts = zip(*non_zero_data)
                
                
                # Calcula percentual acumulado
                total_failures = sum(sorted_counts)
                cumulative_percentages = []
                cumulative_sum = 0
                for count in sorted_counts:
                    cumulative_sum += count
                    cumulative_percentages.append((cumulative_sum / total_failures) * 100)
                
                
                # Configura o eixo existente (já foi limpo no início)
                self.pareto_ax.set_facecolor('none')  # Fundo transparente
                
                # Gráfico de barras
                bars = self.pareto_ax.bar(range(len(sorted_tests)), sorted_counts, color='#ff4444', alpha=0.7, edgecolor='black', linewidth=0.5)
                # Remove o rótulo do eixo X
                # self.pareto_ax.set_xlabel('Testes', fontsize=10)
                self.pareto_ax.set_ylabel(t('fastsurance.number_of_failures'), color='#ff4444', fontsize=10)
                self.pareto_ax.tick_params(axis='y', labelcolor='#ff4444')
                self.pareto_ax.set_xticks(range(len(sorted_tests)))
                test_label = t('fastsurance.test')
                self.pareto_ax.set_xticklabels([f'{test_label} {test_num}' for test_num in sorted_tests])
                
                # Linha de percentual acumulado
                ax2 = self.pareto_ax.twinx()
                line = ax2.plot(range(len(sorted_tests)), cumulative_percentages, color='#4444ff', marker='o', linewidth=1, markersize=6)
                ax2.set_ylabel(t('fastsurance.accumulated'), color='#4444ff', fontsize=10)
                ax2.tick_params(axis='y', labelcolor='#4444ff')
                ax2.set_ylim(0, 120)  # Escala até 120% para dar mais espaço
                ax2.set_facecolor('none')  # Fundo transparente para o segundo eixo
                
                # Define os ticks do eixo Y para mostrar apenas até 100%
                ax2.set_yticks([0, 20, 40, 60, 80, 100])
                ax2.set_yticklabels(['0', '20', '40', '60', '80', '100'])
                
                # Adiciona valores nas barras (sem bold)
                for i, (bar, count) in enumerate(zip(bars, sorted_counts)):
                    height = bar.get_height()
                    self.pareto_ax.text(bar.get_x() + bar.get_width()/2., height + 0.1,
                            f'{count}', ha='center', va='bottom', fontsize=9)
                
                # Adiciona valores na linha (sem bold)
                for i, (x, y) in enumerate(zip(range(len(sorted_tests)), cumulative_percentages)):
                    ax2.text(x, y + 2, f'{y:.1f}%', ha='center', va='bottom', fontsize=8, color='#4444ff')
                
                # Remove o título redundante (já existe no LabelFrame)
                # self.pareto_ax.set_title('Gráfico de Pareto das Falhas', fontsize=12, pad=20)
                
                # Grid
                self.pareto_ax.grid(True, alpha=0.3)
                
                # Ajusta o layout para evitar cortes
                self.pareto_figure.tight_layout(pad=2.0)
                
            else:
                # Se não há falhas, deixa o gráfico completamente vazio
                self.pareto_ax.clear()
                self.pareto_ax.set_facecolor('white')
                self.pareto_ax.set_xticks([])
                self.pareto_ax.set_yticks([])
                self.pareto_ax.axis('off')
                self.pareto_ax.set_title('')
                self.pareto_ax.set_xlabel('')
                self.pareto_ax.set_ylabel('')
            
            # Atualiza o canvas
            self.pareto_canvas.draw()
            
        except Exception as e:
            fc_log("fastsurance", f"Erro ao atualizar gráfico de Pareto: {e}")
            traceback.print_exc()
    
    def _load_persistent_data(self):
        """
        Carrega dados persistentes do arquivo JSON
        """
        try:
            fc_log("fastsurance", f"🔍 FastSurance: Tentando carregar dados de {self.db_file}")
            if os.path.exists(self.db_file):
                with open(self.db_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.test_history = data.get('test_history', [])
                    self.tags_testadas = data.get('tags_testadas', 0)
                    self.tags_aprovadas = data.get('tags_aprovadas', 0)
                    self.tags_reprovadas = data.get('tags_reprovadas', 0)
                    
                    # NOVO: Carrega dados do threshold
                    self.last_threshold_test_data = data.get('last_threshold_test_data', None)
                    self.threshold_graph_data = data.get('threshold_graph_data', [])
                    self.selected_epc = data.get('selected_epc', None)
                    self.saved_slider_values = data.get('slider_values', [])  # NOVO: Valores dos sliders
                    self.saved_tag_test_frequencies = data.get('tag_test_frequencies', [None, None, None, None, None])  # NOVO: Frequências dos testes de tag
                    self.saved_threshold_frequencies = data.get('threshold_frequencies', [None, None, None, None, None])  # NOVO: Frequências dos testes de threshold
                    
                    saved_safety_margins = data.get('safety_margins', None)
                    if saved_safety_margins is None:
                        old_margin = data.get('safety_margin', 0.2)
                        try:
                            old_margin = float(old_margin)
                            if old_margin < 0.0 or old_margin > 5.0:
                                old_margin = 0.2
                        except (ValueError, TypeError):
                            old_margin = 0.2
                        saved_safety_margins = [old_margin] * 5
                    if isinstance(saved_safety_margins, list) and len(saved_safety_margins) == 5 and hasattr(self, 'safety_margin_vars'):
                        for i, margin_val in enumerate(saved_safety_margins):
                            try:
                                margin_val = float(margin_val)
                            except (ValueError, TypeError):
                                margin_val = 0.2
                            if margin_val < 0.0 or margin_val > 5.0:
                                margin_val = 0.2
                            if i < len(self.safety_margin_vars):
                                self.safety_margin_vars[i].set(margin_val)
                    
                    fc_log("fastsurance", f"✅ FastSurance: Dados persistentes carregados - {len(self.test_history)} testes")
                    fc_log("fastsurance", f"   - Frequências de tag carregadas: {self.saved_tag_test_frequencies}")
                    fc_log("fastsurance", f"   - Frequências de threshold carregadas: {self.saved_threshold_frequencies}")
                    fc_log("fastsurance", f"   - Tags testadas: {self.tags_testadas}")
                    fc_log("fastsurance", f"   - Aprovadas: {self.tags_aprovadas}")
                    fc_log("fastsurance", f"   - Reprovadas: {self.tags_reprovadas}")
                    if self.last_threshold_test_data:
                        fc_log("fastsurance", f"   - Dados de threshold: {len(self.last_threshold_test_data)} pontos")
                    if self.threshold_graph_data:
                        fc_log("fastsurance", f"   - Dados do gráfico: {len(self.threshold_graph_data)} pontos")
            else:
                fc_log("fastsurance", f"ℹ️ FastSurance: Arquivo {self.db_file} não encontrado, iniciando com dados vazios")
                self.saved_tag_test_frequencies = [None, None, None, None, None]
                self.saved_threshold_frequencies = [None, None, None, None, None]
                self.saved_slider_values = []
                if hasattr(self, 'safety_margin_vars'):
                    for var in self.safety_margin_vars:
                        var.set(0.2)
        except Exception as e:
            fc_log("fastsurance", f"⚠️ FastSurance: Erro ao carregar dados persistentes: {e}")
            traceback.print_exc()
            self.test_history = []
            self.tags_testadas = 0
            self.tags_aprovadas = 0
            self.tags_reprovadas = 0
            self.saved_tag_test_frequencies = [None, None, None, None, None]
            self.saved_threshold_frequencies = [None, None, None, None, None]
            self.saved_slider_values = []
            if hasattr(self, 'safety_margin_vars'):
                for var in self.safety_margin_vars:
                    var.set(0.2)
    
    def _save_persistent_data(self):
        """
        Salva dados persistentes no arquivo JSON
        - Acumula todos os testes no histórico
        """
        try:
            # Verifica se há dados para salvar
            if not hasattr(self, 'test_history'):
                fc_log("fastsurance", "⚠️ FastSurance: test_history não existe, não há dados para salvar")
                return
            
            # Garante que as variáveis de threshold existam
            if not hasattr(self, 'last_threshold_test_data'):
                self.last_threshold_test_data = None
            if not hasattr(self, 'threshold_graph_data'):
                self.threshold_graph_data = []
            if not hasattr(self, 'selected_epc'):
                self.selected_epc = None
            
            # NOVO: Salva valores atuais dos sliders
            slider_values = []
            if hasattr(self, 'power_vars'):
                for power_var in self.power_vars:
                    slider_values.append(power_var.get())
            
            # NOVO: Salva frequências dos testes 1 a 5 (tag_test_freq_entries)
            tag_test_frequencies = []
            try:
                if hasattr(self, 'tag_test_freq_entries') and self.tag_test_freq_entries:
                    for entry in self.tag_test_freq_entries:
                        try:
                            if entry.winfo_exists():
                                freq_value = entry.get().strip()
                                tag_test_frequencies.append(freq_value if freq_value else None)
                            else:
                                tag_test_frequencies.append(None)
                        except Exception:
                            tag_test_frequencies.append(None)
                else:
                    tag_test_frequencies = [None] * 5
            except Exception as e:
                fc_log("fastsurance", f"⚠️ Erro ao salvar frequências de tag: {e}")
                tag_test_frequencies = [None] * 5
            
            # NOVO: Salva frequências dos threshold (threshold_freq_entries)
            threshold_frequencies = []
            try:
                if hasattr(self, 'threshold_freq_entries') and self.threshold_freq_entries:
                    for entry in self.threshold_freq_entries:
                        try:
                            if entry.winfo_exists():
                                freq_value = entry.get().strip()
                                threshold_frequencies.append(freq_value if freq_value else None)
                            else:
                                threshold_frequencies.append(None)
                        except Exception:
                            threshold_frequencies.append(None)
                else:
                    threshold_frequencies = [None] * 5
            except Exception as e:
                fc_log("fastsurance", f"⚠️ Erro ao salvar frequências de threshold: {e}")
                threshold_frequencies = [None] * 5
            
            # Salva o histórico completo (com acúmulo)
            data = {
                'test_history': self.test_history,  # Histórico completo dos testes
                'tags_testadas': self.tags_testadas,  # Contadores atuais
                'tags_aprovadas': self.tags_aprovadas,
                'tags_reprovadas': self.tags_reprovadas,
                'last_threshold_test_data': self.last_threshold_test_data,  # NOVO: Dados do threshold
                'threshold_graph_data': self.threshold_graph_data,  # NOVO: Dados do gráfico
                'selected_epc': self.selected_epc,  # NOVO: EPC selecionado
                'slider_values': slider_values,  # NOVO: Valores dos sliders
                'tag_test_frequencies': tag_test_frequencies,  # NOVO: Frequências dos testes de tag
                'threshold_frequencies': threshold_frequencies,  # NOVO: Frequências dos testes de threshold
                'safety_margins': [float(var.get()) for var in self.safety_margin_vars] if hasattr(self, 'safety_margin_vars') else [0.2] * 5,
                'last_saved': datetime.now().strftime('%d-%m-%Y %H:%M')
            }
            
            # Cria o diretório se não existir
            os.makedirs(os.path.dirname(self.db_file), exist_ok=True)
            
            # Salva os dados
            with open(self.db_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
            
            fc_log("fastsurance", f"💾 FastSurance: Dados salvos persistentemente em {self.db_file}")
            fc_log("fastsurance", f"   - Testes: {len(self.test_history)}")
            fc_log("fastsurance", f"   - Tags testadas: {self.tags_testadas}")
            fc_log("fastsurance", f"   - Dados threshold: {len(self.last_threshold_test_data) if self.last_threshold_test_data else 0} pontos")
            fc_log("fastsurance", f"   - Dados gráfico: {len(self.threshold_graph_data)} pontos")
            fc_log("fastsurance", f"   - EPC selecionado: {self.selected_epc}")
            fc_log("fastsurance", f"   - Aprovadas: {self.tags_aprovadas}")
            fc_log("fastsurance", f"   - Reprovadas: {self.tags_reprovadas}")
            
        except Exception as e:
            fc_log("fastsurance", f"❌ FastSurance: Erro ao salvar dados persistentes: {e}")
            traceback.print_exc()
    
    def _restore_ui_from_persistent_data(self):
        """
        Restaura a interface com os dados persistentes carregados
        """
        try:
            fc_log("fastsurance", f"🔄 FastSurance: Tentando restaurar interface com {len(self.test_history)} testes")
            fc_log("fastsurance", f"   - test_history existe: {hasattr(self, 'test_history')}")
            fc_log("fastsurance", f"   - history_tree existe: {hasattr(self, 'history_tree')}")
            
            if not self.test_history:
                fc_log("fastsurance", "ℹ️ FastSurance: Nenhum teste para restaurar")
                return
            
            # Verifica se a interface está pronta
            if not hasattr(self, 'history_tree') or not self.history_tree.winfo_exists():
                fc_log("fastsurance", "⚠️ FastSurance: Interface não está pronta, aguardando...")
                self.after(1000, self._restore_ui_from_persistent_data)
                return
            
            fc_log("fastsurance", f"✅ FastSurance: Interface pronta, restaurando {len(self.test_history)} testes")
            
            # Recarrega a tabela de histórico
            for item in self.history_tree.get_children():
                self.history_tree.delete(item)
            
            restored_count = 0
            for i, history_item in enumerate(self.test_history):
                try:
                    fc_log("fastsurance", f"   - Restaurando teste {i+1}: EPC {history_item.get('epc', 'N/A')}")
                    self.add_item_to_history_table(history_item)
                    restored_count += 1
                except Exception as e:
                    fc_log("fastsurance", f"⚠️ FastSurance: Erro ao restaurar item {i+1}: {e}")
                    traceback.print_exc()
            
            # Atualiza o resumo
            if hasattr(self, 'update_summary_display'):
                fc_log("fastsurance", "   - Atualizando resumo...")
                self.update_summary_display()
            
            fc_log("fastsurance", f"✅ FastSurance: Interface restaurada com {restored_count} testes")
            
        except Exception as e:
            fc_log("fastsurance", f"⚠️ FastSurance: Erro ao restaurar interface: {e}")
            traceback.print_exc()
    
    def _start_auto_save(self):
        """
        Inicia o salvamento automático periódico dos dados
        """
        try:
            # Salva dados a cada 30 segundos
            self.after(30000, self._auto_save_cycle)
            fc_log("fastsurance", "🔄 FastSurance: Salvamento automático iniciado (30s)")
        except Exception as e:
            fc_log("fastsurance", f"⚠️ FastSurance: Erro ao iniciar salvamento automático: {e}")
    
    def _auto_save_cycle(self):
        """
        Ciclo de salvamento automático
        """
        try:
            if hasattr(self, 'test_history') and self.test_history:
                self._save_persistent_data()
            
            # Agenda o próximo salvamento
            self.after(30000, self._auto_save_cycle)
        except Exception as e:
            fc_log("fastsurance", f"⚠️ FastSurance: Erro no ciclo de salvamento automático: {e}")

    def update_license_status(self, new_license_status):
        """NOVO: Atualiza o status da licença e a interface do usuário"""
        try:
            fc_log("fastsurance", f"🔔 FastSurance: Atualizando status da licença para {new_license_status}")
            
            # Atualiza o status interno
            if new_license_status:
                # Com licença: obtém limites do app_shell
                if hasattr(self, 'app_shell') and self.app_shell:
                    self._update_license_limits_from_app_shell()
                else:
                    # Fallback: licença padrão
                    self.license_limits = {
                        'min_freq': DEFAULT_MIN_FREQ_MHZ, 'max_freq': DEFAULT_MAX_FREQ_MHZ,
                        'min_power': DEFAULT_MIN_POWER_DBM, 'max_power': DEFAULT_MAX_POWER_DBM,
                        'is_licensed': True
                    }
            else:
                # Sem licença: modo browser
                self.license_limits = {
                    'min_freq': DEFAULT_MIN_FREQ_MHZ, 'max_freq': DEFAULT_MAX_FREQ_MHZ,
                    'min_power': DEFAULT_MIN_POWER_DBM, 'max_power': DEFAULT_MAX_POWER_DBM,
                    'is_licensed': False
                }
            
            # Atualiza a interface
            self.update_ui_state()
            
            fc_log("fastsurance", f"✅ FastSurance: Status da licença atualizado com sucesso")
            
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao atualizar status da licença no FastSurance: {e}")
    
    def update_threshold_graph(self, freq, threshold):
        """NOVO: Atualiza o gráfico de threshold com novos dados"""
        try:
            # Adiciona novo ponto aos dados
            self.threshold_graph_data.append({'freq': freq, 'threshold': threshold})
            
            # Atualiza o gráfico
            self._redraw_threshold_graph()
            
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao atualizar gráfico de threshold: {e}")
    
    def _redraw_threshold_graph(self):
        """NOVO: Redesenha o gráfico de threshold"""
        try:
            # Limpa o gráfico
            self.threshold_ax.clear()
            
            # Configura o gráfico
            self.threshold_ax.set_xlabel(t('fastsurance.frequency_label'))
            self.threshold_ax.set_ylabel(t('fastsurance.threshold_label'))
            self.threshold_ax.set_ylim(5, 25)
            self.threshold_ax.grid(True, alpha=0.3)
            
            # Plota os dados se existirem
            if self.threshold_graph_data:
                freqs = [point['freq'] for point in self.threshold_graph_data]
                thresholds = [point['threshold'] for point in self.threshold_graph_data]
                
                # Ordena por frequência
                sorted_data = sorted(zip(freqs, thresholds))
                freqs, thresholds = zip(*sorted_data)
                
                # Cria índices sequenciais para o eixo X (1, 2, 3, 4, 5...)
                x_indices = list(range(1, len(freqs) + 1))
                
                # Plota linha com índices sequenciais
                self.threshold_ax.plot(x_indices, thresholds, 'b-o', linewidth=2, markersize=4)
                self.threshold_ax.set_xlim(0.5, len(freqs) + 0.5)
                
                # Define ticks do eixo X como índices sequenciais
                self.threshold_ax.set_xticks(x_indices)
                self.threshold_ax.set_xticklabels([f"{freq:.0f}" for freq in freqs], rotation=0)
                
                # Armazena dados para o tooltip (mantém frequências para exibição)
                self.threshold_data = {'freq': list(freqs), 'threshold': list(thresholds), 'indices': list(x_indices)}
                
                fc_log("fastsurance", f"✅ Gráfico atualizado com {len(freqs)} pontos sequenciais de threshold")
            else:
                # Mostra mensagem quando não há dados
                self.threshold_ax.text(0.5, 0.5, 'Nenhum dado de threshold disponível\nExecute um teste de threshold para ver o gráfico', 
                                     transform=self.threshold_ax.transAxes, ha='center', va='center',
                                     fontsize=12, color='gray', alpha=0.7)
                self.threshold_ax.set_xlim(840, 960)  # Limites padrão
                
                # Limpa dados do tooltip
                self.threshold_data = {'freq': [], 'threshold': []}
                fc_log("fastsurance", f"ℹ️ Gráfico sem dados - aguardando teste de threshold")
            
            # Recria a anotação após limpar o gráfico
            self._create_tooltip_annotation()
            
            # Redesenha o canvas
            self.threshold_canvas.draw_idle()
            
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao redesenhar gráfico de threshold: {e}")



    def clear_threshold_graph(self):
        """NOVO: Limpa os dados do gráfico de threshold"""
        try:
            self.threshold_graph_data = []
            self._redraw_threshold_graph()
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao limpar gráfico de threshold: {e}")
    
    def _restore_persistent_interface(self):
        """NOVO: Restaura a interface com dados persistentes carregados"""
        try:
            fc_log("fastsurance", f"🔄 FastSurance: Iniciando restauração da interface...")
            fc_log("fastsurance", f"   - last_threshold_test_data: {self.last_threshold_test_data}")
            fc_log("fastsurance", f"   - threshold_graph_data: {self.threshold_graph_data}")
            fc_log("fastsurance", f"   - selected_epc: {self.selected_epc}")
            fc_log("fastsurance", f"   - test_history: {len(self.test_history) if self.test_history else 0} registros")
            
            # Verifica se os widgets existem
            if not hasattr(self, 'threshold_freq_entries') or not self.threshold_freq_entries:
                fc_log("fastsurance", f"⚠️ FastSurance: threshold_freq_entries não existe ainda, aguardando...")
                self.after(500, self._restore_persistent_interface)
                return
            
            # Restaura dados de threshold se existirem
            if self.last_threshold_test_data:
                fc_log("fastsurance", f"🔄 FastSurance: Restaurando dados de threshold na interface")
                fc_log("fastsurance", f"   - Dados: {self.last_threshold_test_data}")
                
                # Preenche os campos de frequência e threshold
                for i, result in enumerate(self.last_threshold_test_data):
                    if i < len(self.threshold_freq_entries):
                        freq = result.get('freq', 0)
                        threshold = result.get('threshold', 0)
                        fc_log("fastsurance", f"   - Restaurando Teste {i+1}: Freq={freq}, Threshold={threshold}")
                        
                        # Limpa e preenche campo de frequência
                        self.threshold_freq_entries[i].delete(0, 'end')
                        self.threshold_freq_entries[i].insert(0, str(freq))
                        
                        # Limpa e preenche campo de threshold
                        if i < len(self.threshold_power_entries):
                            self.threshold_power_entries[i].config(state="normal")
                            self.threshold_power_entries[i].delete(0, 'end')
                            self.threshold_power_entries[i].insert(0, str(threshold))
                            self.threshold_power_entries[i].config(state="disabled")
                            fc_log("fastsurance", f"   - Campo {i+1} preenchido com threshold: {threshold}")
                        
                        # NOVO: Restaura valor do slider correspondente
                        if i < len(self.power_vars):
                            max_power = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
                            min_power = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
                            clamped_power = max(min(threshold, max_power), min_power)
                            self.power_vars[i].set(clamped_power)
                            fc_log("fastsurance", f"   - Slider {i+1} restaurado para: {clamped_power} dBm")
                
                # NOVO: Atualiza os labels dos sliders
                self.sync_power_displays()
                fc_log("fastsurance", f"✅ FastSurance: Labels dos sliders atualizados")
                
                # Restaura EPC selecionado
                if self.selected_epc:
                    fc_log("fastsurance", f"🔄 FastSurance: Restaurando EPC selecionado: {self.selected_epc}")
                    self.epc_combobox['values'] = [self.selected_epc]
                    self.epc_combobox.set(self.selected_epc)
                    selected_text = t('fastsurance.selected_epc').split(':')[0]
                    self.epc_status_label.config(text=f"{selected_text}: {self.selected_epc}", fg="green")
            
            # Se houve qualquer threshold inválido no último teste, não restaura gráfico
            if self.threshold_graph_data and self.last_threshold_test_data:
                any_invalid = False
                min_p = self.license_limits.get('min_power', DEFAULT_MIN_POWER_DBM)
                max_p = self.license_limits.get('max_power', DEFAULT_MAX_POWER_DBM)
                for result in self.last_threshold_test_data:
                    thr = result.get('threshold')
                    is_str = isinstance(thr, str)
                    try:
                        val = float(str(thr).replace('>', '').replace('<', '').strip())
                    except Exception:
                        val = None
                    if val is None or val < min_p or val > max_p or (is_str and (str(thr).startswith('>') or str(thr).startswith('<'))):
                        any_invalid = True
                        break
                if any_invalid:
                    fc_log("fastsurance", "ℹ️ FastSurance: Threshold inválido detectado no último teste — gráfico não será restaurado")
                    self.clear_threshold_graph()
                else:
                    fc_log("fastsurance", f"🔄 FastSurance: Restaurando gráfico com {len(self.threshold_graph_data)} pontos")
                    self._redraw_threshold_graph()
            
            # NOVO: Restaura histórico de tags testadas
            if self.test_history:
                fc_log("fastsurance", f"🔄 FastSurance: Restaurando histórico de tags testadas com {len(self.test_history)} registros")
                # Limpa a tabela atual
                for item in self.history_tree.get_children():
                    self.history_tree.delete(item)
                # Adiciona cada item do histórico
                for test_record in self.test_history:
                    self.add_item_to_history_table(test_record)
                fc_log("fastsurance", f"✅ FastSurance: {len(self.test_history)} registros adicionados à tabela de histórico")
            
            # NOVO: Restaura contadores do resumo da execução
            fc_log("fastsurance", f"🔄 FastSurance: Restaurando contadores do resumo")
            fc_log("fastsurance", f"   - Tags testadas: {self.tags_testadas}")
            fc_log("fastsurance", f"   - Aprovadas: {self.tags_aprovadas}")
            fc_log("fastsurance", f"   - Reprovadas: {self.tags_reprovadas}")
            
            # Atualiza as variáveis StringVar para a interface
            self.tags_testadas_var.set(str(self.tags_testadas))
            self.tags_aprovadas_var.set(str(self.tags_aprovadas))
            self.tags_reprovadas_var.set(str(self.tags_reprovadas))
            
            # Calcula e atualiza o yield
            if self.tags_testadas > 0:
                yield_percent = (self.tags_aprovadas / self.tags_testadas) * 100
                self.yield_var.set(f"{yield_percent:.1f}%")
            else:
                self.yield_var.set("0.0%")
            
            fc_log("fastsurance", f"✅ FastSurance: Contadores do resumo restaurados")
            
            # NOVO: Após restaurar histórico, tenta restaurar frequências do último teste se não houver frequências explícitas salvas
            if self.test_type_var.get() == "TAG_TESTS":
                self.after(500, self._restore_tag_test_frequencies)
            
            fc_log("fastsurance", f"✅ FastSurance: Interface restaurada com dados persistentes")
            
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao restaurar interface persistente: {e}")
            traceback.print_exc()
    
    def _restore_tag_test_frequencies(self):
        """NOVO: Restaura apenas as frequências dos testes de tag quando o modo TAG_TESTS é ativado"""
        try:
            fc_log("fastsurance", f"🔄 FastSurance: Tentando restaurar frequências dos testes de tag...")
            
            # Verifica se os widgets existem
            if not hasattr(self, 'tag_test_freq_entries') or not self.tag_test_freq_entries:
                fc_log("fastsurance", f"⚠️ FastSurance: Campos de frequência de tag não existem ainda, aguardando...")
                self.after(500, self._restore_tag_test_frequencies)
                return
            
            # Verifica se saved_tag_test_frequencies existe e tem dados
            if not hasattr(self, 'saved_tag_test_frequencies'):
                fc_log("fastsurance", f"⚠️ FastSurance: saved_tag_test_frequencies não existe")
                return
            
            fc_log("fastsurance", f"   - Frequências carregadas do arquivo: {self.saved_tag_test_frequencies}")
            
            # Verifica se há pelo menos uma frequência não nula para restaurar
            has_frequencies = any(freq for freq in self.saved_tag_test_frequencies if freq and str(freq).strip())
            
            # NOVO: Se não houver frequências salvas explicitamente, tenta restaurar do último teste do histórico
            if not has_frequencies and hasattr(self, 'test_history') and self.test_history:
                fc_log("fastsurance", f"ℹ️ FastSurance: Sem frequências explícitas salvas, tentando restaurar do último teste do histórico...")
                last_test = self.test_history[-1]
                if 'test_params' in last_test and last_test['test_params']:
                    fc_log("fastsurance", f"   - Encontrado último teste com test_params: {len(last_test['test_params'])} parâmetros")
                    # Restaura frequências do último teste
                    for i, params in enumerate(last_test['test_params']):
                        if i < 5 and i < len(self.saved_tag_test_frequencies):
                            freq_val = params.get('freq', '')
                            if freq_val:
                                self.saved_tag_test_frequencies[i] = str(freq_val)
                                fc_log("fastsurance", f"   - Frequência {i+1} do histórico: {freq_val}")
                    # Verifica novamente se agora temos frequências
                    has_frequencies = any(freq for freq in self.saved_tag_test_frequencies if freq and str(freq).strip())
            
            if not has_frequencies:
                fc_log("fastsurance", f"ℹ️ FastSurance: Nenhuma frequência válida encontrada para restaurar")
                return
            
            fc_log("fastsurance", f"   - Encontradas frequências válidas para restaurar")
            restored_count = 0
            skipped_count = 0
            
            # Restaura frequências dos testes de tag (testes 1 a 5)
            for i, freq_value in enumerate(self.saved_tag_test_frequencies):
                if i >= len(self.tag_test_freq_entries):
                    fc_log("fastsurance", f"   ⚠️ Teste {i+1}: Índice fora do range dos campos disponíveis")
                    continue
                    
                if not freq_value or not str(freq_value).strip():
                    fc_log("fastsurance", f"   ℹ️ Teste {i+1}: Sem frequência salva (None ou vazio)")
                    continue
                
                try:
                    entry = self.tag_test_freq_entries[i]
                    # Verifica se o widget existe
                    if not entry.winfo_exists():
                        fc_log("fastsurance", f"   ⚠️ Teste {i+1}: Widget não existe")
                        continue
                    
                    # Verifica se o campo já tem um valor antes de restaurar
                    current_value = entry.get().strip()
                    freq_str = str(freq_value).strip()
                    
                    # Só restaura se o campo estiver vazio ou se o valor salvo for diferente
                    if not current_value or current_value != freq_str:
                        entry.delete(0, 'end')
                        entry.insert(0, freq_str)
                        fc_log("fastsurance", f"   ✅ Teste {i+1}: Frequência restaurada de '{current_value}' para '{freq_str}'")
                        restored_count += 1
                    else:
                        fc_log("fastsurance", f"   ℹ️ Teste {i+1}: Frequência já está correta ('{current_value}')")
                        skipped_count += 1
                except Exception as e:
                    fc_log("fastsurance", f"   ❌ Teste {i+1}: Erro ao restaurar frequência '{freq_value}': {e}")
                    traceback.print_exc()
            
            fc_log("fastsurance", f"✅ FastSurance: Restauração concluída - {restored_count} restauradas, {skipped_count} já corretas")
        except Exception as e:
            fc_log("fastsurance", f"❌ FastSurance: Erro ao restaurar frequências dos testes de tag: {e}")
            traceback.print_exc()
    
    def _check_epc_not_duplicate(self):
        """NOVO: Verifica se o EPC selecionado já foi testado anteriormente"""
        try:
            if not self.selected_epc:
                fc_log("fastsurance", "⚠️ FastSurance: Nenhum EPC selecionado para verificação de duplicata")
                return True
            
            # Verifica se o EPC já existe no histórico de testes
            for test_record in self.test_history:
                if test_record.get('epc') == self.selected_epc:
                    # EPC já foi testado - mostra popup de rejeição
                    fc_log("fastsurance", f"❌ FastSurance: EPC {self.selected_epc} já foi testado - REJEITANDO")
                    self._show_duplicate_epc_popup(self.selected_epc, test_record)
                    return False
            
            fc_log("fastsurance", f"✅ FastSurance: EPC {self.selected_epc} não foi testado anteriormente - APROVADO")
            return True
            
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao verificar EPC duplicado: {e}")
            return True  # Em caso de erro, permite o teste
    
    def _show_duplicate_epc_popup(self, epc, test_record):
        """NOVO: Mostra popup de alerta para EPC já testado"""
        try:
            # Obtém informações do teste anterior
            raw_status_anterior = test_record.get('status', 'DESCONHECIDO')
            passed_text = t('fastsurance.passed')
            failed_text = t('fastsurance.failed')
            # Normaliza status para exibição (aceita valores antigos e novos)
            if raw_status_anterior in ['PASSOU', 'Passou', passed_text]:
                status_anterior = passed_text
            elif raw_status_anterior in ['REPROVADO', 'Reprovado', failed_text]:
                status_anterior = failed_text
            else:
                status_anterior = raw_status_anterior
            failed_freqs = test_record.get('failed_freqs', [])
            
            # Monta mensagem do popup
            message = f"⚠️ EPC JÁ TESTADO ⚠️\n\n"
            message += f"O EPC {epc} já foi testado anteriormente.\n\n"
            message += f"Resultado anterior: {status_anterior}\n"
            
            if failed_freqs:
                message += f"Frequências que falharam: {', '.join(failed_freqs)} MHz\n"
            
            message += f"\nEste teste será REJEITADO.\n"
            message += f"Clique em 'OK' para cancelar o teste."
            
            # Cria popup personalizado
            popup = tk.Toplevel(self.master)
            popup.title("EPC Já Testado")
            popup.geometry("500x300")
            popup.resizable(False, False)
            popup.transient(self.master)
            popup.grab_set()
            
            # Centraliza o popup
            popup.update_idletasks()
            x = (popup.winfo_screenwidth() // 2) - (500 // 2)
            y = (popup.winfo_screenheight() // 2) - (300 // 2)
            popup.geometry(f"500x300+{x}+{y}")
            
            # Frame principal
            main_frame = tk.Frame(popup, padx=20, pady=20)
            main_frame.pack(fill="both", expand=True)
            
            # Ícone de aviso
            warning_label = tk.Label(main_frame, text="⚠️", font=("Arial", 48), fg="#ff6b6b")
            warning_label.pack(pady=(0, 10))
            
            # Título
            title_label = tk.Label(main_frame, text="EPC JÁ TESTADO", 
                                 font=("Arial", 16), fg="#ff6b6b")
            title_label.pack(pady=(0, 10))
            
            # Mensagem
            message_label = tk.Label(main_frame, text=f"O EPC {epc} já foi testado anteriormente.", 
                                   font=("Arial", 12), wraplength=450)
            message_label.pack(pady=(0, 5))
            
            # Status anterior
            status_label = tk.Label(main_frame, text=f"Resultado anterior: {status_anterior}", 
                                  font=("Arial", 11), 
                                  fg="#27ae60" if status_anterior == t('fastsurance.passed') else "#e74c3c")
            status_label.pack(pady=(0, 5))
            
            # Frequências que falharam
            if failed_freqs:
                failed_label = tk.Label(main_frame, text=f"Frequências que falharam: {', '.join(failed_freqs)} MHz", 
                                      font=("Arial", 10), fg="#e74c3c")
                failed_label.pack(pady=(0, 10))
            
            # Aviso importante
            warning_text = tk.Label(main_frame, text="Este teste será REJEITADO.", 
                                  font=("Arial", 11), fg="#e74c3c")
            warning_text.pack(pady=(0, 20))
            
            # Botão único
            def close_popup():
                popup.destroy()
                # Reabilita o botão de teste
                if hasattr(self, 'start_button'):
                    self.start_button.config(state="normal", text="Testar")
                # Para o teste atual
                if self.app_shell:
                    self.app_shell.set_test_running(False)
            
            cancel_button = tk.Button(main_frame, text="OK - Entendi", 
                                    command=close_popup, font=("Arial", 12),
                                    bg="#e74c3c", fg="white", width=20, height=2)
            cancel_button.pack(pady=10)
            
            fc_log("fastsurance", f"⚠️ FastSurance: Popup de EPC duplicado mostrado para: {epc}")
            
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao mostrar popup de EPC duplicado: {e}")
            traceback.print_exc()
    
    def _is_epc_duplicate(self, epc):
        """NOVO: Verifica se o EPC já existe no histórico (excluindo o teste atual)"""
        try:
            if not epc:
                return False
            
            # Conta quantas vezes este EPC aparece no histórico
            count = 0
            for test_record in self.test_history:
                if test_record.get('epc') == epc:
                    count += 1
            
            # Se aparece mais de uma vez, é duplicado
            return count > 0
            
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao verificar se EPC é duplicado: {e}")
            return False
    
    def _create_tooltip_annotation(self):
        """Cria ou recria a anotação do tooltip"""
        try:
            self.threshold_annot = self.threshold_ax.annotate("", xy=(0, 0), xytext=(20, 20), textcoords="offset points",
                                                              bbox=dict(boxstyle="round,pad=0.5", 
                                                                       facecolor="lightblue", 
                                                                       alpha=0.9,
                                                                       edgecolor="navy",
                                                                       linewidth=1),
                                                              arrowprops=dict(arrowstyle="->", 
                                                                             connectionstyle="arc3,rad=0",
                                                                             color="navy",
                                                                             lw=1),
                                                              fontsize=9, 
                                                              ha='left',
                                                              va='bottom',
                                                              wrap=True)
            self.threshold_annot.set_visible(False)
            fc_log("fastsurance", f"✅ Anotação do tooltip criada/recriada com sucesso")
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao criar anotação: {e}")
            self.threshold_annot = None
    
    def _position_tooltip_smartly(self, event, x_pos, y_pos, tooltip_text):
        """Posiciona o tooltip de forma inteligente baseado na posição do mouse"""
        try:
            # Obtém dimensões do canvas em pixels
            canvas_width = self.threshold_canvas.get_width_height()[0]
            canvas_height = self.threshold_canvas.get_width_height()[1]
            
            # Converte coordenadas do gráfico para pixels
            x_pixel, y_pixel = self.threshold_ax.transData.transform((x_pos, y_pos))
            
            # Determina posição do tooltip baseado na posição do mouse
            if x_pixel > canvas_width * 0.7:  # Mouse na direita
                xytext = (-20, 20)  # Tooltip à esquerda
            else:  # Mouse na esquerda
                xytext = (20, 20)   # Tooltip à direita
            
            # Atualiza a anotação
            self.threshold_annot.xy = (x_pos, y_pos)
            self.threshold_annot.set_text(tooltip_text)
            self.threshold_annot.xytext = xytext
            self.threshold_annot.set_visible(True)
            
            # Redesenha o canvas
            self.threshold_canvas.draw_idle()
            
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro ao posicionar tooltip: {e}")
    
    def on_mouse_move(self, event):
        """Evento de movimento do mouse sobre o gráfico"""
        try:
            # Verifica se a anotação existe
            if not hasattr(self, 'threshold_annot') or self.threshold_annot is None:
                return
            
            # Verifica se está dentro do gráfico
            if event.inaxes != self.threshold_ax:
                # Esconde tooltip se estiver visível
                if self.threshold_annot.get_visible():
                    self.threshold_annot.set_visible(False)
                    self.threshold_canvas.draw_idle()
                return
            
            # Verifica se o mouse está sobre uma linha da curva
            for line in self.threshold_ax.lines:
                cont, ind = line.contains(event)
                if cont:
                    # Mouse está sobre um ponto da linha
                    pos = line.get_xydata()[ind["ind"][0]]
                    x_coord, y_coord = pos[0], pos[1]
                    
                    # Formata o texto do tooltip
                    tooltip_text = f"Frequência: {x_coord:.1f} MHz\nThreshold: {y_coord:.1f} dBm"
                    
                    # Posiciona o tooltip
                    self._position_tooltip_smartly(event, x_coord, y_coord, tooltip_text)
                    return
            
            # Se chegou aqui, mouse não está sobre nenhuma linha
            if self.threshold_annot.get_visible():
                self.threshold_annot.set_visible(False)
                self.threshold_canvas.draw_idle()
                
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro no on_mouse_move: {e}")
            import traceback
            traceback.print_exc()
    
    def on_mouse_leave(self, event):
        """Evento quando o mouse sai do gráfico"""
        try:
            if hasattr(self, 'threshold_annot') and self.threshold_annot is not None:
                self.threshold_annot.set_visible(False)
                self.threshold_canvas.draw_idle()
        except Exception as e:
            fc_log("fastsurance", f"❌ Erro no on_mouse_leave: {e}")

if __name__ == "__main__":
    try:
        root = tk.Tk()
        # Para teste standalone, precisamos encontrar a porta primeiro
        from port_manager import get_com_port_number
        com_port = get_com_port_number()
        if com_port is None:
            messagebox.showerror(t('fastsurance.hardware_not_found'), t('fastsurance.hardware_not_found_msg'))
            root.destroy()
        else:
            app = RFIDApp(root, com_port=com_port)
            root.mainloop()
    except Exception as e:
        traceback.print_exc()
        messagebox.showerror(t('fastsurance.critical_error'), t('fastsurance.critical_error_msg').format(error=str(e)))