# license_module.py
# Versão: 3.6 (Seleção de Licença Ativa) - MODIFICADO PARA PORTA COM AUTOMÁTICA
# Data: 25/06/2025 19:25
# Descrição: Adiciona UI para selecionar uma licença ativa entre múltiplas licenças.

import tkinter as tk
from tkinter import ttk, messagebox, filedialog, simpledialog
import ctypes, os, sys, json, threading, gzip, base64, requests, traceback, time

from .app_console import fc_log
try:
    from Crypto.Cipher import DES
    from Crypto.Hash import MD5
    from Crypto.Util.Padding import pad, unpad
    from PIL import Image, ImageTk
except ImportError:
    fc_log("license", "AVISO: Bibliotecas 'pycryptodome' ou 'Pillow' não encontradas.")

# --- Constantes ---
DLL_NAME = "UHFRFID.dll"
# COM_PORT = 4 ### ALTERADO: Constante removida, a porta agora é recebida dinamicamente.
BAUD_RATE = 115200
VALIDATION_URL = "http://gft.cpss.com.br/validate"
COLOR_BG, COLOR_SECTION_BG, COLOR_TEXT = "#F5F5F5", "#EAEAEA", "#333333"
COLOR_GREEN, COLOR_RED, COLOR_BLUE = "#2E7D32", "#C62828", "#005B9A"
RFID_CMD_GET_HARDWARE_VERSION, RFID_CMD_GET_FIRMWARE_VERSION, RFID_CMD_GET_MODULE_ID = 0x00, 0x02, 0x04
RFID_CMD_SET_SOFTRESET = 0x68
RFID_CMD_SET_FACTORYRESTORE = 0x74
RFID_CMD_SET_CW_STATUS = 0x24
RFID_CMD_GET_CW_STATUS = 0x26
RFID_CMD_SET_REGION = 0x2C
RFID_CMD_GET_REGION = 0x2E
RFID_CMD_SET_TEMPPROTECT = 0x38
RFID_CMD_GET_TEMPPROTECT = 0x3A
RFID_CMD_SET_TXPOWER = 0x10
RFID_CMD_GET_TXPOWER = 0x12
RFID_CMD_SET_FREQ_TABLE = 0x14
RFID_CMD_SET_BUZZER = 0x56
RFID_CMD_SET_SOUND_LIGHT = 0x7A

# Importa configuração de versão centralizada
try:
    from .version_config import get_software_info
    SOFTWARE_INFO = get_software_info()
except ImportError:
    # Fallback caso o arquivo não exista
    SOFTWARE_INFO = {'software': '3.1.2', 'name': 'FastChecker II', 'version': '3.1.2'}

# Sistema de traduções
from .i18n import get_translator, t

class HardwareManager:
    ### ALTERADO: O construtor agora aceita o número da porta COM. ###
    def __init__(self, com_port=4): # O valor 4 serve como padrão para testes isolados
        self.rfid_sdk = None
        self.com_port = com_port ### NOVO: Armazena a porta COM da instância.
        self._last_profile_snapshot = {}
        try:
            if hasattr(sys, '_MEIPASS'):
                dll_path = os.path.join(sys._MEIPASS, DLL_NAME)
            else:
                dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), DLL_NAME)
            self.rfid_sdk = ctypes.CDLL(dll_path)
            self._configure_functions()
        except OSError:
            self.rfid_sdk = None
            fc_log("license", f"AVISO: Não foi possível carregar a DLL '{DLL_NAME}' no HardwareManager.")

    def _configure_functions(self):
        self.rfid_sdk.UHF_RFID_Open.argtypes = [ctypes.c_ubyte, ctypes.c_int]
        self.rfid_sdk.UHF_RFID_Open.restype = ctypes.c_int
        self.rfid_sdk.UHF_RFID_Close.argtypes = [ctypes.c_ubyte]
        self.rfid_sdk.UHF_RFID_Close.restype = ctypes.c_int
        self.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)]
        self.rfid_sdk.UHF_RFID_Set.restype = ctypes.c_int

    def try_detect_and_set_port(self, max_port=30):
        """Tenta detectar a porta varrendo COM1..COM{max_port} usando a própria DLL.
        Se encontrar, atualiza self.com_port e retorna True.
        """
        if not self.rfid_sdk:
            return False
        # 1) Tenta via port_manager com refresh
        try:
            from . import port_manager as _pm
        except Exception:
            _pm = None
        if _pm is not None:
            candidate = _pm.get_com_port_number(refresh=True)
            if isinstance(candidate, int) and candidate > 0:
                # Valida abrindo
                try:
                    if self.rfid_sdk.UHF_RFID_Open(candidate, BAUD_RATE) == 0:
                        # valida com leitura de UID
                        out_buf = ctypes.create_string_buffer(32)
                        out_len = ctypes.c_uint(0)
                        status = self.rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_MODULE_ID, None, 0, out_buf, ctypes.byref(out_len))
                        try:
                            self.rfid_sdk.UHF_RFID_Close(candidate)
                        except Exception:
                            pass
                        if status == 0 and out_len.value >= 4:
                            self.com_port = candidate
                            return True
                except Exception:
                    pass
        # 2) Varrendo COMs (otimizado: prioriza COMs vizinhas)
        # Primeiro tenta COMs próximas à detectada pelo Registro
        if _pm is not None:
            registry_port = _pm.get_com_port_number(refresh=False)
            if isinstance(registry_port, int) and registry_port > 0:
                # Tenta COMs vizinhas: registry_port-2, registry_port-1, registry_port, registry_port+1, registry_port+2
                for offset in [-2, -1, 0, 1, 2]:
                    port = registry_port + offset
                    if 1 <= port <= max_port:
                        try:
                            if self.rfid_sdk.UHF_RFID_Open(port, BAUD_RATE) == 0:
                                # valida com leitura de UID
                                out_buf = ctypes.create_string_buffer(32)
                                out_len = ctypes.c_uint(0)
                                status = self.rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_MODULE_ID, None, 0, out_buf, ctypes.byref(out_len))
                                try:
                                    self.rfid_sdk.UHF_RFID_Close(port)
                                except Exception:
                                    pass
                                if status == 0 and out_len.value >= 4:
                                    self.com_port = port
                                    return True
                        except Exception:
                            continue
        # Se não encontrou nas vizinhas, varre todas
        for port in range(1, max_port + 1):
            try:
                if self.rfid_sdk.UHF_RFID_Open(port, BAUD_RATE) == 0:
                    # valida com leitura de UID
                    out_buf = ctypes.create_string_buffer(32)
                    out_len = ctypes.c_uint(0)
                    status = self.rfid_sdk.UHF_RFID_Set(RFID_CMD_GET_MODULE_ID, None, 0, out_buf, ctypes.byref(out_len))
                    try:
                        self.rfid_sdk.UHF_RFID_Close(port)
                    except Exception:
                        pass
                    if status == 0 and out_len.value >= 4:
                        self.com_port = port
                        return True
            except Exception:
                continue
        return False

    def connect(self):
        if not self.rfid_sdk: return False
        try:
            # Conexão direta sem retries para resposta rápida
            try:
                self.rfid_sdk.UHF_RFID_Close(self.com_port)
            except Exception:
                pass
            status = self.rfid_sdk.UHF_RFID_Open(self.com_port, BAUD_RATE)
            if status == 0:
                # Define o atributo port para indicar que está conectado
                self.port = self.com_port
                return True
            # Falha rápida - não tenta redescoberta
            return False
        except Exception:
            return False

    def close(self):
        if self.rfid_sdk:
            try:
                ### ALTERADO: Usa a porta da instância (self.com_port) em vez da constante. ###
                self.rfid_sdk.UHF_RFID_Close(self.com_port)
                # Limpa o atributo port para indicar desconexão
                if hasattr(self, 'port'):
                    delattr(self, 'port')
            except Exception:
                pass

    def soft_reset(self, retries: int = 3, retry_delay: float = 0.6):
        """Executa soft reset no hardware - ABRE PORTA, FAZ RESET COM BIP, FECHA PORTA"""
        if not self.rfid_sdk:
            fc_log("license", "[ERRO] soft_reset: rfid_sdk nao disponivel")
            return False
        
        for attempt in range(1, retries + 1):
            try:
                fc_log("license", f"[INFO] soft_reset: Tentativa {attempt} abrindo COM{self.com_port}...")
                if self.rfid_sdk.UHF_RFID_Open(self.com_port, 115200) != 0:
                    fc_log("license", f"[AVISO] soft_reset: Falha ao abrir porta COM{self.com_port} (tentativa {attempt})")
                    time.sleep(retry_delay)
                    continue
                
                try:
                    out_buf = ctypes.create_string_buffer(16)
                    out_len = ctypes.c_uint(0)
                    status = self.rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_SOFTRESET, None, 0, out_buf, ctypes.byref(out_len))
                    
                    if status == 0:
                        fc_log("license", "[OK] soft_reset: Comando de reset enviado com sucesso - BIP!")
                        return True
                    else:
                        fc_log("license", f"[AVISO] soft_reset: Comando retornou status {status} (tentativa {attempt})")
                finally:
                    self.rfid_sdk.UHF_RFID_Close(self.com_port)
                    fc_log("license", "[INFO] soft_reset: Porta fechada")
            except Exception as e:
                fc_log("license", f"[ERRO] soft_reset: Excecao na tentativa {attempt}: {e}")
            
            time.sleep(retry_delay)
        
        fc_log("license", "[ERRO] soft_reset: Todas as tentativas falharam.")
        return False

    def soft_reset_if_needed(self, cooldown_ms=1500):
        """Executa soft reset no máximo 1 vez por janela de cooldown."""
        now_ms = int(time.time() * 1000)
        last = getattr(self, "_last_soft_reset_ms", 0)
        if now_ms - last < cooldown_ms:
            return True
        ok = self.soft_reset()
        if ok:
            self._last_soft_reset_ms = now_ms
        return ok

    def _execute_command(self, command, payload: bytes = None, output_size: int = 256, return_status: bool = False):
        """Abstrai chamadas à DLL. Pode retornar apenas dados ou (status, dados)."""
        if not self.rfid_sdk:
            return (-1, b"") if return_status else None

        output_buffer = ctypes.create_string_buffer(output_size)
        output_len = ctypes.c_uint(0)

        try:
            if payload is None or len(payload) == 0:
                status = self.rfid_sdk.UHF_RFID_Set(
                    command,
                    None,
                    0,
                    output_buffer,
                    ctypes.byref(output_len)
                )
            else:
                status = self.rfid_sdk.UHF_RFID_Set(
                    command,
                    ctypes.c_char_p(payload),
                    len(payload),
                    output_buffer,
                    ctypes.byref(output_len)
                )
        except Exception as e:
            fc_log("license", f"[ERRO] _execute_command falhou: cmd=0x{command:02X} err={e}")
            return (-1, b"") if return_status else None

        data = output_buffer.raw[:output_len.value] if output_len.value > 0 else b""
        if return_status:
            return status, data
        return data

    def _parse_tx_power_response(self, data: bytes):
        """Interpreta resposta do comando GET_TXPOWER."""
        if not data:
            return None

        status_byte = data[0]
        entries = []
        idx = 1

        while idx + 4 < len(data):
            antenna = data[idx]
            read_raw = int.from_bytes(data[idx + 1:idx + 3], "big", signed=True)
            write_raw = int.from_bytes(data[idx + 3:idx + 5], "big", signed=True)

            entries.append({
                "antenna": antenna,
                "read_x100": read_raw,
                "write_x100": write_raw
            })
            idx += 5

        return {
            "status_byte": status_byte,
            "entries": entries
        }

    def capture_operational_profile(self):
        """Lê parâmetros críticos para restaurar após um factory reset."""
        profile = {}

        if not self.connect():
            fc_log("license", "[AVISO] capture_operational_profile: impossível conectar ao reader.")
            return profile

        try:
            status, data = self._execute_command(RFID_CMD_GET_TXPOWER, None, 128, return_status=True)
            if status == 0 and data:
                tx_profile = self._parse_tx_power_response(data)
                if tx_profile:
                    profile["tx_power"] = tx_profile

            status, data = self._execute_command(RFID_CMD_GET_REGION, None, 16, return_status=True)
            if status == 0 and data:
                # Data[0] indica sucesso; Data[1] contém o código da região
                region_code = data[-1]
                profile["region"] = {"code": region_code, "keep_flag": 1}

            status, data = self._execute_command(RFID_CMD_GET_TEMPPROTECT, None, 8, return_status=True)
            if status == 0 and data:
                profile["temp_protection"] = bool(data[-1])

            status, data = self._execute_command(RFID_CMD_GET_CW_STATUS, None, 8, return_status=True)
            if status == 0 and data:
                profile["cw_status"] = bool(data[-1])

        finally:
            self.close()

        if profile:
            self._last_profile_snapshot = profile
        return profile

    def _restore_tx_power(self, tx_profile: dict):
        """Restaura potência por antena."""
        if not tx_profile or "entries" not in tx_profile:
            return True

        status_byte = tx_profile.get("status_byte", 0x00)
        success = True

        for entry in tx_profile["entries"]:
            antenna = entry.get("antenna")
            read_raw = entry.get("read_x100")
            write_raw = entry.get("write_x100")

            if antenna is None or read_raw is None or write_raw is None:
                continue

            payload = bytes([
                status_byte,
                antenna
            ]) + int(read_raw).to_bytes(2, "big", signed=True) + int(write_raw).to_bytes(2, "big", signed=True)

            status, ack = self._execute_command(RFID_CMD_SET_TXPOWER, payload, 16, return_status=True)
            if status != 0 or (ack and ack[0] == 0x00):
                success = False
            time.sleep(0.05)

        return success

    def _restore_region(self, region_profile: dict):
        if not region_profile or "code" not in region_profile:
            return True

        keep_flag = region_profile.get("keep_flag", 1) & 0x01
        region_code = region_profile["code"]
        payload = bytes([keep_flag, region_code])

        status, ack = self._execute_command(RFID_CMD_SET_REGION, payload, 16, return_status=True)
        if status != 0:
            return False
        if ack and ack[0] == 0x00:
            return False
        return True

    def _restore_temp_protection(self, enabled: bool):
        payload = bytes([0x01 if enabled else 0x00])
        status, ack = self._execute_command(RFID_CMD_SET_TEMPPROTECT, payload, 16, return_status=True)
        if status != 0:
            return False
        if ack and ack[0] == 0x00:
            return False
        return True

    def _restore_cw_status(self, enabled: bool):
        payload = bytes([0x01 if enabled else 0x00])
        status, ack = self._execute_command(RFID_CMD_SET_CW_STATUS, payload, 16, return_status=True)
        if status != 0:
            return False
        if ack and ack[0] == 0x00:
            return False
        return True

    def restore_operational_profile(self, profile: dict):
        """Aplica novamente as configurações salvas."""
        if not profile:
            return True

        if not self.connect():
            fc_log("license", "[AVISO] restore_operational_profile: impossível reconectar ao reader.")
            return False

        overall_success = True
        try:
            if "region" in profile:
                if not self._restore_region(profile["region"]):
                    overall_success = False

            if "tx_power" in profile:
                if not self._restore_tx_power(profile["tx_power"]):
                    overall_success = False

            if "temp_protection" in profile:
                if not self._restore_temp_protection(profile["temp_protection"]):
                    overall_success = False

            if "cw_status" in profile:
                if not self._restore_cw_status(profile["cw_status"]):
                    overall_success = False

        finally:
            self.close()

        return overall_success

    def factory_reset(self, preserve_profile: bool = True, cool_down_seconds: float = 0.6, emit_beep: bool = True):
        """Executa factory reset e restaura configuração anterior se solicitado."""
        if not self.rfid_sdk:
            fc_log("license", "[ERRO] factory_reset: DLL não carregada.")
            return False

        profile = {}
        if preserve_profile:
            profile = self.capture_operational_profile()
            if not profile:
                fc_log("license", "[AVISO] factory_reset: falha ao capturar perfil atual, prosseguindo mesmo assim.")

        if not self.connect():
            fc_log("license", f"[ERRO] factory_reset: não conseguiu abrir COM{self.com_port}.")
            return False

        out_buf = ctypes.create_string_buffer(32)
        out_len = ctypes.c_uint(0)

        try:
            # Commands de segurança antes do reset
            try:
                self.rfid_sdk.UHF_RFID_Set(0x8C, None, 0, out_buf, ctypes.byref(out_len))  # STOP INVENTORY
            except Exception:
                pass

            try:
                self.rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_CW_STATUS, ctypes.c_char_p(b'\x00'), 1, out_buf, ctypes.byref(out_len))
            except Exception:
                pass

            status = self.rfid_sdk.UHF_RFID_Set(
                RFID_CMD_SET_FACTORYRESTORE,
                None,
                0,
                out_buf,
                ctypes.byref(out_len)
            )
        except Exception as e:
            fc_log("license", f"[ERRO] factory_reset: exceção ao enviar comando: {e}")
            status = -1
        finally:
            self.rfid_sdk.UHF_RFID_Close(self.com_port)
            if hasattr(self, "port"):
                delattr(self, "port")

        if status != 0:
            fc_log("license", f"[ERRO] factory_reset: comando retornou status {status}")
            return False

        fc_log("license", f"[INFO] Factory reset enviado com sucesso. Aguardando {cool_down_seconds:.1f}s para reinício...")
        time.sleep(max(cool_down_seconds, 0.3))

        if preserve_profile and profile:
            restored = self.restore_operational_profile(profile)
            if not restored:
                fc_log("license", "[AVISO] factory_reset: restauração do perfil apresentou falhas.")
            else:
                fc_log("license", "[OK] factory_reset: perfil operacional restaurado.")

        # Reconfigura o buzzer para garantir que os próximos resets emitam som
        self.ensure_buzzer_enabled()

        # Pequena espera adicional para garantir que o leitor esteja pronto antes do soft reset
        time.sleep(0.8)

        # Emite bip pós-reset para manter compatibilidade com o comportamento antigo
        if emit_beep:
            self.soft_reset()

        self._last_profile_snapshot = profile
        return True

    def get_hardware_version(self):
        """Método otimizado para leitura de hardware version"""
        # Tenta múltiplas vezes com pequenos delays
        for attempt in range(3):
            try:
                # Pequeno delay entre tentativas
                if attempt > 0:
                    time.sleep(0.1)
                
                response = self._execute_command(RFID_CMD_GET_HARDWARE_VERSION)
                
                if response and len(response) >= 2:
                    # Formato esperado: 2 bytes (major.minor)
                    major = response[0]
                    minor = response[1]
                    version = f"{major}.{minor}"
                    return version
                    
            except Exception:
                pass
        
        return None

    def get_hardware_version_3_digits(self):
        """Método para leitura de hardware version com 3 dígitos (major.minor.patch)"""
        # Tenta múltiplas vezes com pequenos delays
        for attempt in range(3):
            try:
                # Pequeno delay entre tentativas
                if attempt > 0:
                    time.sleep(0.1)
                
                response = self._execute_command(RFID_CMD_GET_HARDWARE_VERSION)
                
                if response and len(response) >= 3:
                    # Formato esperado: 3 bytes (major.minor.patch)
                    major = response[0]
                    minor = response[1]
                    patch = response[2]
                    version = f"{major}.{minor}.{patch}"
                    return version
                elif response and len(response) >= 2:
                    # Fallback para 2 dígitos se não houver terceiro byte
                    major = response[0]
                    minor = response[1]
                    version = f"{major}.{minor}.0"  # Adiciona .0 como patch
                    return version
                    
            except Exception:
                pass
        
        return None

    def get_firmware_version(self):
        response = self._execute_command(RFID_CMD_GET_FIRMWARE_VERSION)
        if response and len(response) >= 3: return f"{response[0]}.{response[1]}.{response[2]}"
        return None

    def get_uid(self):
        # MODIFICADO: Leitura direta sem retries para resposta rápida
        response = self._execute_command(RFID_CMD_GET_MODULE_ID)
        if response and len(response) >= 4:
            return str(int.from_bytes(response[:4], 'big'))
        return None

    def trigger_feedback(self):
        if not self.rfid_sdk:
            return False

        needs_close = False
        try:
            if not hasattr(self, 'port'):
                if not self.connect():
                    return self.soft_reset()
                needs_close = True

            status, _ = self._execute_command(
                RFID_CMD_SET_BUZZER,
                b'\x00\x00',
                16,
                return_status=True
            )
            if status == 0:
                return True
            else:
                fc_log("license", f"[AVISO] trigger_feedback: comando de buzzer retornou status {status}, tentando soft reset.")
                return self.soft_reset()
        except Exception as e:
            fc_log("license", f"[AVISO] trigger_feedback: exceção ao acionar buzzer ({e}), tentando soft reset.")
            return self.soft_reset()
        finally:
            if needs_close:
                self.close()

    def ensure_buzzer_enabled(self):
        """Configura o reader para manter o alarme sonoro ligado mesmo após resets."""
        payload = bytes([0x01,  # DByte0: salvar após power-off
                         0x01,  # DByte1: habilita voz/buzzer
                         0x01]  # DByte2: habilita indicador luminoso
                        + [0x00] * 8)  # Demais bytes reservados

        if not self.connect():
            fc_log("license", "[AVISO] ensure_buzzer_enabled: não foi possível abrir a porta para configurar som.")
            return False

        try:
            status, _ = self._execute_command(
                RFID_CMD_SET_SOUND_LIGHT,
                payload,
                32,
                return_status=True
            )
            if status != 0:
                fc_log("license", f"[AVISO] ensure_buzzer_enabled: comando retornou status {status}")
                return False
            fc_log("license", "[OK] ensure_buzzer_enabled: buzzer configurado para permanecer ativo.")
            return True
        except Exception as e:
            fc_log("license", f"[ERRO] ensure_buzzer_enabled: exceção ao configurar sound/light ({e})")
            return False
        finally:
            self.close()


class LicenseManager:
    _password = "#C0Pp1Si5t3m@s!".encode('utf-8')
    _iterations, _salt_size = 1000, 8

    def __init__(self, db_file):
        self.db_file = db_file
        self.licenses = self._load_and_migrate_db()

    def check_any_valid_license(self):
        """CORREÇÃO: Verifica se há pelo menos uma licença válida, não apenas se existem entradas"""
        try:
            for uid, tokens in self.licenses.items():
                if isinstance(tokens, list):
                    for token in tokens:
                        lic_data, _ = self.parse_and_validate_token(token)
                        if lic_data:
                            return True
                elif isinstance(tokens, str):
                    lic_data, _ = self.parse_and_validate_token(tokens)
                    if lic_data:
                        return True
            
            return False
            
        except Exception as e:
            return False

    def _load_and_migrate_db(self):
        try:
            with open(self.db_file, 'r') as f: data = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError): return {}
        needs_migration = any(isinstance(value, str) for value in data.values())
        if needs_migration:
            migrated_data = {uid: [value] if isinstance(value, str) else value for uid, value in data.items()}
            self.licenses = migrated_data
            self._save_licenses_to_db()
            return migrated_data
        return data

    def _derive_key_iv(self, salt):
        hasher = MD5.new(); hasher.update(self._password); hasher.update(salt)
        result = hasher.digest()
        for i in range(1, self._iterations):
            hasher = MD5.new(); hasher.update(result); result = hasher.digest()
        return result[:8], result[8:16]

    def _jasypt_pbe_with_md5_and_des_decrypt(self, encrypted_b64):
        try:
            d, s = base64.b64decode(encrypted_b64), self._salt_size
            k, i = self._derive_key_iv(d[:s])
            c = DES.new(k, DES.MODE_CBC, i)
            return unpad(c.decrypt(d[s:]), DES.block_size).decode('utf-8')
        except Exception: return None

    def _save_licenses_to_db(self):
        with open(self.db_file, 'w') as f: json.dump(self.licenses, f, indent=4)

    def parse_and_validate_token(self, token_str):
        """Validação rigorosa de licença baseada no sistema Java original"""
        decrypted_str = self._jasypt_pbe_with_md5_and_des_decrypt(token_str)
        if not decrypted_str: 
            return None, "Token inválido ou corrompido."
        
        try:
            lic_data = json.loads(decrypted_str)
            
            # VALIDAÇÃO RIGOROSA - Baseada no Java original
            validation_errors = []
            
            required_fields = ["licName", "serial", "uid", "sellDate"]
            for field in required_fields:
                if field not in lic_data or not lic_data[field]:
                    validation_errors.append(f"Campo obrigatório '{field}' ausente ou vazio")
            
            forbidden_values = ["unknown", "Unknown", "UNKNOWN"]
            for field in ["licName", "serial", "uid"]:
                if field in lic_data and lic_data[field] in forbidden_values:
                    validation_errors.append(f"Campo '{field}' não pode ser 'unknown'")
            
            if "freqRange" not in lic_data or not lic_data["freqRange"]:
                validation_errors.append("Faixa de frequência (freqRange) obrigatória")
            else:
                try:
                    freq_ranges = lic_data["freqRange"].split(";")
                    for freq_range in freq_ranges:
                        if "@" not in freq_range:
                            validation_errors.append(f"Formato de frequência inválido: {freq_range}")
                        else:
                            parts = freq_range.split("@")
                            if len(parts) != 2:
                                validation_errors.append(f"Formato de frequência inválido: {freq_range}")
                            else:
                                freq_min = int(parts[0])
                                freq_max = int(parts[1])
                                if freq_min <= 0 or freq_max <= 0:
                                    validation_errors.append(f"Frequências devem ser > 0: {freq_range}")
                                if freq_min >= freq_max:
                                    validation_errors.append(f"Frequência mínima deve ser < máxima: {freq_range}")
                except (ValueError, IndexError) as e:
                    validation_errors.append(f"Erro ao processar frequências: {e}")
            
            if "powRange" not in lic_data or not lic_data["powRange"]:
                validation_errors.append("Faixa de potência (powRange) obrigatória")
            else:
                try:
                    pow_parts = lic_data["powRange"].split("@")
                    if len(pow_parts) != 2:
                        validation_errors.append(f"Formato de potência inválido: {lic_data['powRange']}")
                    else:
                        pow_min = int(pow_parts[0])
                        pow_max = int(pow_parts[1])
                        if pow_min >= pow_max:
                            validation_errors.append(f"Potência mínima deve ser < máxima: {lic_data['powRange']}")
                except (ValueError, IndexError) as e:
                    validation_errors.append(f"Erro ao processar potência: {e}")
            
            if "powStep" in lic_data:
                try:
                    pow_step = float(lic_data["powStep"])
                    if pow_step <= 0:
                        validation_errors.append("Step de potência deve ser > 0")
                except (ValueError, TypeError):
                    validation_errors.append("Step de potência inválido")
            else:
                validation_errors.append("Step de potência (powStep) obrigatório")
            
            if "sellDate" in lic_data:
                try:
                    sell_date_str = lic_data["sellDate"]
                    if not sell_date_str or sell_date_str.strip() == "":
                        validation_errors.append("Data de venda não pode ser vazia")
                except Exception as e:
                    validation_errors.append(f"Data de venda inválida: {e}")
            
            if validation_errors:
                error_msg = "Licença inválida:\n" + "\n".join(f"• {error}" for error in validation_errors)
                return None, error_msg
            
            return lic_data, "Licença válida"
            
        except json.JSONDecodeError: 
            return None, "Formato do token (JSON) inválido."
        except Exception as e:
            return None, f"Erro inesperado na validação: {e}"

    def get_licenses_by_uid(self, uid): return self.licenses.get(str(uid), [])

    def save_license(self, uid, token):
        uid_str = str(uid)
        if uid_str not in self.licenses: self.licenses[uid_str] = []
        if token not in self.licenses[uid_str]: self.licenses[uid_str].append(token)
        self._save_licenses_to_db()

    def delete_license(self, uid, token_to_delete):
        uid_str = str(uid)
        if uid_str in self.licenses and token_to_delete in self.licenses[uid_str]:
            self.licenses[uid_str].remove(token_to_delete)
            if not self.licenses[uid_str]: del self.licenses[uid_str]
            self._save_licenses_to_db(); return True
        return False

    def unzip_license_file(self, filepath):
        """Descompacta arquivo de licença"""
        try:
            with open(filepath, 'rb') as f:
                compressed_data = f.read()
            
            if not compressed_data:
                return None
                
            # Descompacta usando gzip
            decompressed_data = gzip.decompress(compressed_data)
            return decompressed_data.decode('utf-8')
            
        except Exception as e:
            fc_log("license", f"Erro ao descompactar arquivo: {e}")
            return None

    def zip_license_file(self, token, fp):
        try:
            with gzip.open(fp, 'wt', encoding='utf-8') as f: f.write(token); return True
        except Exception: return False

    def auto_license(self, auth_token, uid):
        if not auth_token: return None, "Token de autorização não pode ser vazio."
        try:
            enc_uid = self._jasypt_pbe_with_md5_and_des_decrypt(uid).decode('utf-8')
            payload = {'uid': enc_uid, 'token': auth_token}
            response = requests.post(VALIDATION_URL, data=payload, timeout=15)
            response.raise_for_status()
            res_json = response.json()
            if res_json.get("valid"): return res_json.get("msg"), "Licença obtida com sucesso."
            else: return None, f"Falha no registro: {res_json.get('msg', 'Erro desconhecido.')}"
        except requests.exceptions.RequestException as e: return None, f"Erro de comunicação: {e}"
        except Exception as e: return None, f"Erro inesperado: {e}"

    def get_active_license_system_info(self, com_port=None):
        """
        Obtém informações do sistema da licença ativa para uso em relatórios
        
        Returns:
            dict: Informações do sistema (software, hardware, firmware, serial_number, license, generated_at)
        """
        try:
            from datetime import datetime
            
            # Informações básicas - formata data conforme idioma
            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'
            system_info = {
                'software': SOFTWARE_INFO['software'],
                'hardware': 'N/A',
                'firmware': 'N/A',
                'serial_number': 'N/A',
                'license': 'N/A',
                'generated_at': datetime.now().strftime(date_format)
            }
            
            # Tenta obter informações do hardware se com_port fornecido
            if com_port:
                try:
                    hw_manager = HardwareManager(com_port=com_port)
                    
                    if hw_manager.connect():
                        # Tenta obter cada informação individualmente com tratamento de erro
                        try:
                            hw_version = hw_manager.get_hardware_version_3_digits()
                            if hw_version:
                                system_info['hardware'] = f"v{hw_version}"
                        except Exception as hw_error:
                            pass
                        
                        try:
                            fw_version = hw_manager.get_firmware_version()
                            if fw_version:
                                system_info['firmware'] = f"v{fw_version}"
                        except Exception as fw_error:
                            pass
                        
                        try:
                            uid = hw_manager.get_uid()
                            if uid:
                                # UID obtido com sucesso, mas será substituído pelo serial da licença se disponível
                                pass
                        except Exception as uid_error:
                            pass
                        
                        hw_manager.close()
                    else:
                        pass
                        
                except Exception as e:
                    pass
            else:
                pass
            
            # Obtém informações da licença ativa
            try:
                # Tenta obter via app_shell primeiro
                active_license_name = None
                
                # Verifica se há um app_shell disponível no contexto atual
                import inspect
                frame = inspect.currentframe()
                while frame:
                    if 'self' in frame.f_locals:
                        obj = frame.f_locals['self']
                        if hasattr(obj, 'app_shell') and obj.app_shell and hasattr(obj.app_shell, 'active_license_token'):
                            active_token = obj.app_shell.active_license_token
                            if active_token:
                                lic_data, msg = self.parse_and_validate_token(active_token)
                                if lic_data:
                                    active_license_name = lic_data.get('licName', 'Licença Ativa')
                                    # Obtém o serial number da licença
                                    license_serial = lic_data.get('serial', 'N/A')
                                    if license_serial and license_serial != 'N/A':
                                        system_info['serial_number'] = license_serial
                            else:
                                pass
                            break
                    frame = frame.f_back
                
                if active_license_name:
                    system_info['license'] = active_license_name
                else:
                    system_info['license'] = 'Modo Browser'
                    
            except Exception as e:
                system_info['license'] = 'N/A'
            
            return system_info
            
        except Exception as e:
            # Fallback com formatação de data conforme idioma
            from .i18n import get_translator
            translator = get_translator()
            date_format_fallback = '%d/%m/%Y %H:%M:%S' if translator.get_language() == 'pt' else '%m/%d/%y %I:%M:%S %p'
            return {
                'software': 'FastChecker II',
                'hardware': 'N/A',
                'firmware': 'N/A',
                'serial_number': 'N/A',
                'license': 'N/A',
                'generated_at': datetime.now().strftime(date_format_fallback)
            }


class LicenseScreen(tk.Frame):
    """
    Tela de gerenciamento de licenças com sistema de timeout inteligente.
    
    COMPORTAMENTO CORRIGIDO (v5.5.2):
    1) Se não houver comunicação com reader → Modo Browser
    2) Se encontrar reader mas não tiver licença → Modo Browser  
    3) Se encontrar reader e tiver licença → Funcionamento normal
    
    FUNCIONALIDADES:
    - Para de tentar conectar após apenas 1 tentativa TOTAL
    - Sistema de controle centralizado evita retries paralelos
    - Botão "Reload System Information" faz reset completo do módulo
    - Botões de licença só funcionam com reader conectado
    - Resposta INSTANTÂNEA (sem delays, sem retries)
    - Modo browser para demonstrações sem hardware
    - CORREÇÃO: Hardware detectado se firmware OU hardware for lido (firmware é suficiente)
    """
    def __init__(self, parent, hw_manager, license_manager, app_shell=None):
        super().__init__(parent)
        self.hw_manager, self.license_manager, self.uid = hw_manager, license_manager, None
        self.app_shell = app_shell
        self.selected_token_var = tk.StringVar()
        self.translator = get_translator()
        # Armazena referências aos widgets para atualização de idioma
        self._widget_refs = {}
        self._create_widgets()
        # MODIFICADO: Sistema de controle centralizado para evitar retries paralelos
        self._auto_retries_left = 1
        self._auto_started = False
        self._initial_feedback_done = False
        self._connection_attempts_made = 0  # NOVO: Contador total de tentativas
        self._max_total_attempts = 1       # NOVO: Máximo total de tentativas
        self._license_feedback_emitted = False
        # NOVO: Usa port_manager centralizado para evitar conflitos
        try:
            from . import port_manager as _pm
            self.port_manager = _pm.COMPortManager()
        except Exception:
            self.port_manager = None
        # NOVO: Não inicializa automaticamente - apenas quando app_shell chamar start_auto_load
        # self.after(150, self.load_device_information)  # REMOVIDO
        self.bind("<Destroy>", self.on_destroy)

    def start_auto_load(self):
        """Inicialização controlada pelo app_shell"""
        if not self._auto_started:
            self._auto_started = True
            # Reset do sistema de controle centralizado
            self._auto_retries_left = 1
            self._connection_attempts_made = 0

            # Executa factory reset apenas uma vez na inicialização do módulo
            try:
                already_done = getattr(self.hw_manager, "_factory_reset_done", False)
                if not already_done and not HardwareManager._factory_reset_global_done:
                    fc_log("license", "[INFO] LicenseScreen: executando factory reset inicial com restauração de perfil.")
                    if self.hw_manager.factory_reset(preserve_profile=True):
                        fc_log("license", "[OK] LicenseScreen: factory reset inicial concluído.")
                    else:
                        fc_log("license", "[AVISO] LicenseScreen: factory reset inicial falhou, prosseguindo com carregamento.")
                    self.hw_manager._factory_reset_done = True
                else:
                    fc_log("license", "[INFO] LicenseScreen: factory reset já executado anteriormente, pulando.")
            except Exception as e:
                fc_log("license", f"[AVISO] LicenseScreen: erro ao tentar factory reset inicial: {e}")

            # Pequeno delay para garantir que a UI esteja pronta
            self.after(100, self.load_device_information)
        else:
            # Se já foi iniciado, força um reload
            # Reset do sistema de controle centralizado
            self._auto_retries_left = 1
            self._connection_attempts_made = 0
            self.load_device_information()

    def on_destroy(self, event=None):
        # NOVO: Libera a porta usando port_manager se disponível
        if hasattr(self, 'port_manager') and self.port_manager:
            self.port_manager.release_port("LicenseModule")
        self.hw_manager.close()

    def _create_widgets(self):
        main_frame = tk.Frame(self, bg=COLOR_BG); main_frame.pack(fill="both", expand=True, padx=20, pady=10)
        self.main_frame = main_frame  # Guarda referência
        
        self._create_section_title(main_frame, t('license.system_information'))
        sys_info_frame = tk.Frame(main_frame, bg=COLOR_SECTION_BG, pady=15); sys_info_frame.pack(fill="x", pady=(0, 10))
        info_line_frame = tk.Frame(sys_info_frame, bg=COLOR_SECTION_BG); info_line_frame.pack()
        self.sw_var, self.hw_var, self.fw_var = tk.StringVar(value=""), tk.StringVar(value=""), tk.StringVar(value="")
        # CORREÇÃO: Status inicial contextual
        self.status_var = tk.StringVar(value=t('license.status_default'))
        
        sw_label = ttk.Label(info_line_frame, text=t('license.software'), font=("Segoe UI", 9, "bold"), background=COLOR_SECTION_BG)
        sw_label.pack(side="left", padx=(0,5))
        self._widget_refs['sw_label'] = sw_label
        
        ttk.Label(info_line_frame, textvariable=self.sw_var, background=COLOR_SECTION_BG).pack(side="left", padx=(0, 20))
        
        hw_label = ttk.Label(info_line_frame, text=t('license.hardware'), font=("Segoe UI", 9, "bold"), background=COLOR_SECTION_BG)
        hw_label.pack(side="left", padx=(0,5))
        self._widget_refs['hw_label'] = hw_label
        
        ttk.Label(info_line_frame, textvariable=self.hw_var, background=COLOR_SECTION_BG).pack(side="left", padx=(0, 20))
        
        fw_label = ttk.Label(info_line_frame, text=t('license.firmware'), font=("Segoe UI", 9, "bold"), background=COLOR_SECTION_BG)
        fw_label.pack(side="left", padx=(0,5))
        self._widget_refs['fw_label'] = fw_label
        
        ttk.Label(info_line_frame, textvariable=self.fw_var, background=COLOR_SECTION_BG).pack(side="left", padx=(0, 20))
        
        self.status_label = tk.Label(sys_info_frame, textvariable=self.status_var, font=("Segoe UI", 9, "bold"), bg=COLOR_SECTION_BG, fg=COLOR_BLUE)
        self.status_label.pack(pady=(5,10))
        
        reload_btn = ttk.Button(sys_info_frame, text=t('license.reload_button'), command=self.reload_with_reset)
        reload_btn.pack()
        self._widget_refs['reload_btn'] = reload_btn
        
        self._create_section_title(main_frame, t('license.license_information'), 'license_info_title')
        lic_buttons_frame = tk.Frame(main_frame, bg=COLOR_SECTION_BG, pady=15); lic_buttons_frame.pack(fill="x", pady=(0, 10))
        btn_inner_frame = tk.Frame(lic_buttons_frame, bg=COLOR_SECTION_BG); btn_inner_frame.pack()
        
        auto_btn = ttk.Button(btn_inner_frame, text=t('license.auto_license'), command=self.handle_auto_license)
        auto_btn.pack(side="left", padx=5)
        self._widget_refs['auto_btn'] = auto_btn
        
        import_btn = ttk.Button(btn_inner_frame, text=t('license.import_license'), command=self.handle_import_license)
        import_btn.pack(side="left", padx=5)
        self._widget_refs['import_btn'] = import_btn
        
        eula_btn = ttk.Button(btn_inner_frame, text=t('license.eula_agreement'), command=self.handle_eula_agreement)
        eula_btn.pack(side="left", padx=5)
        self._widget_refs['eula_btn'] = eula_btn
        
        self._create_section_title(main_frame, t('license.licenses_on_device'), 'licenses_list_title')
        self._create_scrollable_license_list(main_frame)
    
    def refresh_language(self):
        """Atualiza todos os textos da interface quando o idioma é alterado"""
        try:
            # Atualiza labels
            if 'sw_label' in self._widget_refs:
                self._widget_refs['sw_label'].config(text=t('license.software'))
            if 'hw_label' in self._widget_refs:
                self._widget_refs['hw_label'].config(text=t('license.hardware'))
            if 'fw_label' in self._widget_refs:
                self._widget_refs['fw_label'].config(text=t('license.firmware'))
            
            # Atualiza botões
            if 'reload_btn' in self._widget_refs:
                self._widget_refs['reload_btn'].config(text=t('license.reload_button'))
            if 'auto_btn' in self._widget_refs:
                self._widget_refs['auto_btn'].config(text=t('license.auto_license'))
            if 'import_btn' in self._widget_refs:
                self._widget_refs['import_btn'].config(text=t('license.import_license'))
            if 'eula_btn' in self._widget_refs:
                self._widget_refs['eula_btn'].config(text=t('license.eula_agreement'))
            
            # Atualiza títulos de seção
            if 'sys_info_title' in self._widget_refs:
                self._widget_refs['sys_info_title'].config(text=t('license.system_information'))
            if 'license_info_title' in self._widget_refs:
                self._widget_refs['license_info_title'].config(text=t('license.license_information'))
            if 'licenses_list_title' in self._widget_refs:
                self._widget_refs['licenses_list_title'].config(text=t('license.licenses_on_device'))
            
            # Atualiza status se estiver em estado padrão
            if self.status_var.get() == t('license.status_default') or self.status_var.get() == "Sistema de licenças":
                self.status_var.set(t('license.status_default'))
        except Exception as e:
            fc_log("license", f"⚠️ Erro ao atualizar idioma no License: {e}")

    def _create_section_title(self, parent, title, key=None):
        """Cria um título de seção, guardando referência se key for fornecido"""
        label = tk.Label(parent, text=title, font=("Segoe UI", 12, "bold"), bg=COLOR_BG, fg=COLOR_TEXT)
        label.pack(pady=(15, 5))
        if key:
            self._widget_refs[key] = label
        return label

    def _create_scrollable_license_list(self, parent):
        container = tk.Frame(parent, bg=COLOR_SECTION_BG); container.pack(fill="both", expand=True)
        canvas = tk.Canvas(container, bg=COLOR_BG, highlightthickness=0)
        scrollbar = ttk.Scrollbar(container, orient="vertical", command=canvas.yview)
        self.license_list_frame = tk.Frame(canvas, bg=COLOR_BG)
        self.license_list_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
        canvas.create_window((0, 0), window=self.license_list_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)
        canvas.pack(side="left", fill="both", expand=True); scrollbar.pack(side="right", fill="y")

    def reload_with_reset(self):
        """Chamado quando o usuário clica no botão 'Reload System Information' - faz reset com bip"""
        fc_log("license", "[INFO] Reload System Information (manual) - Executando reset com bip...")
        self._execute_reset_with_beep()
        self.load_device_information()

    def load_device_information(self):
        """Método otimizado para resposta rápida - SEM reset/bip"""
        # CORREÇÃO: Não faz reset aqui - o reset inicial já foi feito pelo app_shell
        # Isso evita múltiplos bips ao iniciar o programa
        fc_log("license", "[INFO] Carregando informações do dispositivo (sem reset)...")
        
        # Reset completo do estado para nova verificação
        self._connection_attempts_made = 0
        self._auto_retries_left = 1
        self.uid = None
        self._license_feedback_emitted = False
        
        # Verificação centralizada de tentativas
        if self._connection_attempts_made >= self._max_total_attempts:
            self.status_var.set(t('license.status_browser_mode'))
            self.status_label.config(fg=COLOR_RED)
            self.hw_var.set("?")
            self.fw_var.set("?")
            self._clear_license_list()
            self._enable_license_buttons(False)
            return
            
        self._connection_attempts_made += 1
        
        # Verificação rápida de hardware disponível
        if not self.hw_manager.rfid_sdk:
            self.status_var.set(t('license.status_browser_mode'))
            self.status_label.config(fg=COLOR_RED)
            self.hw_var.set("?")
            self.fw_var.set("?")
            self._clear_license_list()
            self._enable_license_buttons(False)
            return
            
        self.status_var.set(t('license.status_connecting')); self.status_label.config(fg=COLOR_BLUE); self.update_idletasks()
        self.sw_var.set(SOFTWARE_INFO['software']) # Versão centralizada
        
        # Reset das variáveis de estado
        self.hw_var.set("?")
        self.fw_var.set("?")
        
        # Tentativa de conexão rápida com timeout
        try:
            # Timeout muito curto para conexão
            if not self.hw_manager.connect():
                # Falha rápida - não tenta redescoberta
                self.status_var.set(t('license.status_browser_mode'))
                self.status_label.config(fg=COLOR_RED)
                self.hw_var.set("?")
                self.fw_var.set("?")
                self._clear_license_list()
                self._enable_license_buttons(False)
                return
        except Exception as e:
            self.status_var.set(t('license.status_browser_mode'))
            self.status_label.config(fg=COLOR_RED)
            self.hw_var.set("?")
            self.fw_var.set("?")
            self._clear_license_list()
            self._enable_license_buttons(False)
            return
            
        # Se chegou aqui, hardware está disponível
        threading.Thread(target=self._get_info_thread, daemon=True).start()

    def _get_info_thread_with_port_manager(self):
        """NOVO: Thread para obter informações usando port_manager centralizado"""
        try:
            def _get_info_operation():
                # CORREÇÃO: Não faz reset aqui - o reset inicial já foi feito pelo app_shell
                # Isso evita múltiplos bips ao iniciar o programa
                # Marca como feito para evitar tentativas futuras
                self._initial_feedback_done = True
                hw, fw, uid = self.hw_manager.get_hardware_version_3_digits(), self.hw_manager.get_firmware_version(), self.hw_manager.get_uid()
                self.hw_manager.trigger_feedback()
                return hw, fw, uid
            
            # Executa a operação usando port_manager
            result = self.port_manager.execute_with_port(_get_info_operation, "LicenseModule")
            if result:
                hw, fw, uid = result
            else:
                hw, fw, uid = None, None, None
                
        except Exception as e:
            fc_log("license", f"Erro no thread de informações: {e}")
            hw, fw, uid = None, None, None
        
        # NOVO: Verifica se precisa fazer retry
        if not hw or not fw or not uid:
            # MODIFICADO: Sem retry para resposta instantânea
            pass
            
        self.after(0, self._update_ui_with_info, hw, fw, uid)

    def _execute_reset_with_beep(self):
        """Executa reset do hardware com bip sonoro"""
        try:
            if hasattr(self, 'hw_manager') and self.hw_manager:
                fc_log("license", "[INFO] Executando factory reset com preservação de perfil...")
                if self.hw_manager.factory_reset(preserve_profile=True):
                    fc_log("license", "[OK] Factory reset concluído e perfil restaurado.")
                    return True

                fc_log("license", "[AVISO] Factory reset falhou - tentando soft reset como fallback.")
                if self.hw_manager.soft_reset():
                    fc_log("license", "[OK] Soft reset concluído após falha no factory reset.")
                    time.sleep(0.2)
                    return True

                fc_log("license", "[ERRO] Nenhum tipo de reset pôde ser executado.")
                return False
            else:
                fc_log("license", "[AVISO] Hardware manager nao disponivel para reset")
                return False
        except Exception as e:
            fc_log("license", f"[ERRO] Erro ao executar reset: {e}")
            return False

    def _get_info_thread(self):
        """Thread otimizado para leitura de informações"""
        try:
            # Primeiro tenta conectar
            if not self.hw_manager.connect():
                self.after(0, lambda: self._update_ui_with_info(None, None, None))
                return
                
            # Tenta firmware primeiro (que sabemos que funciona)
            fw_ver = self.hw_manager.get_firmware_version()
            
            # Tenta hardware version (3 dígitos)
            hw_ver = self.hw_manager.get_hardware_version_3_digits()
            
            # Tenta UID
            uid = self.hw_manager.get_uid()
            
            # Fecha conexão
            self.hw_manager.close()
            
            # Envia para UI
            self.after(0, lambda: self._update_ui_with_info(hw_ver, fw_ver, uid))
            
        except Exception as e:
            self.after(0, lambda: self._update_ui_with_info(None, None, None))

    def _update_ui_with_info(self, hw_ver, fw_ver, uid):
        # Atualização explícita das variáveis de hardware
        if hw_ver:
            self.hw_var.set(hw_ver)
        else:
            self.hw_var.set("?")
            
        if fw_ver:
            self.fw_var.set(fw_ver)
        else:
            self.fw_var.set("?")
        
        # Força atualização da interface
        self.update_idletasks()
        
        # Lógica corrigida conforme especificações
        # Hardware detectado se firmware OU hardware for lido (firmware é suficiente)
        if fw_ver or hw_ver:  # Hardware detectado (reader conectado)
            if uid:  # UID lido com sucesso
                self.uid = uid
                token_list = self.license_manager.get_licenses_by_uid(uid)
                if token_list:  # Licença encontrada
                    self.status_var.set(t('license.device_licenced'))
                    self.status_label.config(fg=COLOR_GREEN)
                    # Habilita botões de licença
                    self._enable_license_buttons(True)
                    if not self._license_feedback_emitted:
                        if self.hw_manager.trigger_feedback():
                            fc_log("license", "[OK] LicenseScreen: feedback sonoro emitido para licença carregada.")
                        self._license_feedback_emitted = True
                else:  # Reader conectado mas sem licença
                    self.status_var.set(t('license.device_not_licenced'))
                    self.status_label.config(fg=COLOR_RED)
                    # Permite importar licença mesmo sem UID
                    self._enable_license_buttons(True)
                    if self.app_shell:
                        self.app_shell.set_active_license(None)
                self._populate_license_list(token_list)
            else:  # Reader conectado mas não conseguiu ler UID
                self.status_var.set(t('license.device_connected'))
                self.status_label.config(fg=COLOR_RED)
                # Permite importar licença mesmo sem UID
                self._enable_license_buttons(True)
                self._clear_license_list()
        else:  # Hardware não detectado
            self.status_var.set(t('license.status_browser_mode'))
            self.status_label.config(fg=COLOR_RED)
            self._enable_license_buttons(False)
            self._clear_license_list()

    def _clear_license_list(self):
        for w in self.license_list_frame.winfo_children(): w.destroy()

    def _enable_license_buttons(self, enabled):
        """Habilita/desabilita botões de licença baseado na disponibilidade de hardware"""
        try:
            # Procura pelos botões na interface
            for widget in self.winfo_children():
                if isinstance(widget, tk.Frame):
                    for child in widget.winfo_children():
                        if isinstance(child, tk.Frame):
                            for button in child.winfo_children():
                                if isinstance(button, ttk.Button):
                                    if button.cget("text") in ["Auto License", "Import License", "Acordo da Licença"]:
                                        button.config(state="normal" if enabled else "disabled")
        except Exception:
            pass

    def on_license_selected(self):
        selected_token = self.selected_token_var.get()
        if self.app_shell:
            self.app_shell.set_active_license(selected_token)

    def _populate_license_list(self, token_list):
        self._clear_license_list()

        if self.app_shell:
            # Se não há licença ativa mas há licenças disponíveis, ativa a primeira
            if token_list and not self.app_shell.active_license_token:
                self.app_shell.set_active_license(token_list[0])
            # Define a licença ativa no radio button
            self.selected_token_var.set(self.app_shell.active_license_token)

        for token in token_list:
            lic_data, msg = self.license_manager.parse_and_validate_token(token)
            if not lic_data: continue

            card_frame = tk.Frame(self.license_list_frame, bg=COLOR_SECTION_BG, padx=10, pady=10)
            card_frame.pack(fill="x", pady=5, padx=10)

            selection_frame = tk.Frame(card_frame, bg=COLOR_SECTION_BG)
            selection_frame.pack(side="left", padx=(0, 10), anchor="c")
            rb = ttk.Radiobutton(selection_frame,
                                 variable=self.selected_token_var,
                                 value=token,
                                 command=self.on_license_selected)
            rb.pack()

            details_frame = tk.Frame(card_frame, bg=COLOR_SECTION_BG)
            details_frame.pack(side="left", fill="x", expand=True)
            def add_detail(r, c, label, value):
                ttk.Label(details_frame, text=label, font=("Segoe UI", 9, "bold"), background=COLOR_SECTION_BG).grid(row=r, column=c, sticky="w", padx=(0,5))
                ttk.Label(details_frame, text=value, font=("Segoe UI", 9), background=COLOR_SECTION_BG).grid(row=r, column=c+1, sticky="w", padx=(0, 20))

            add_detail(0, 0, "Serial Number:", lic_data.get("serial", "-"))
            add_detail(1, 0, "License Type:", lic_data.get("licName", "-"))

            freq_parts = lic_data.get('freqRange', '-@-').split('@')
            pow_parts = lic_data.get('powRange', '-@-').split('@')
            
            add_detail(0, 2, "Frequency Range:", f"{freq_parts[0]}-{freq_parts[1]} MHz")
            add_detail(1, 2, "Power Range:", f"{pow_parts[0]}-{pow_parts[1]} dBm")
            
            add_detail(0, 4, "License Issue Date:", lic_data.get("sellDate", "-"))
            add_detail(1, 4, "License Expiration:", lic_data.get("expDate", "-"))
            
            # Destaca visualmente a licença ativa
            if self.app_shell and self.app_shell.active_license_token == token:
                card_frame.config(bg="#E8F5E8")  # Verde claro para licença ativa
                selection_frame.config(bg="#E8F5E8")
                details_frame.config(bg="#E8F5E8")
                # NOVO: Removido indicador "ATIVA" redundante - o radio button já indica qual está ativa
            
            btn_frame = tk.Frame(card_frame, bg=COLOR_SECTION_BG); btn_frame.pack(side="right", padx=(20,0))
            ttk.Button(btn_frame, text="Technical File", command=lambda d=lic_data: self.handle_technical_file(d)).pack(fill="x", pady=2)
            ttk.Button(btn_frame, text="Export", command=lambda t=token, d=lic_data: self.handle_export_license(t, d)).pack(fill="x", pady=2)
            ttk.Button(btn_frame, text="Delete", command=lambda t=token: self.handle_delete_license(t)).pack(fill="x", pady=2)

    def handle_technical_file(self, lic_data):
        if not lic_data: messagebox.showwarning(t('license.no_license_warning'), t('license.no_license_warning_msg'), parent=self); return
        info = f"--- System Information ---\nSoftware: {self.sw_var.get()}\nHardware: {self.hw_var.get()}\nFirmware: {self.fw_var.get()}\nUID: {self.uid}\n\n--- License Information ---\n"
        for k, v in lic_data.items(): info += f"{k.title()}: {v}\n"
        messagebox.showinfo(t('license.technical_sheet'), info, parent=self)

    def handle_export_license(self, token_to_export, lic_data):
        serial = lic_data.get("serial", "license")
        fp = filedialog.asksaveasfilename(title=t('license.export_title'), initialfile=f"{serial}.lic", defaultextension=".lic", filetypes=[(t('license.filetype_lic'), "*.lic")], parent=self)
        if fp and self.license_manager.zip_license_file(token_to_export, fp): messagebox.showinfo(t('license.export_success'), t('license.export_success_msg').format(path=fp), parent=self)
        elif fp: messagebox.showerror(t('license.export_error'), t('license.export_error_msg'), parent=self)

    def handle_delete_license(self, token_to_delete):
        if not hasattr(self, 'uid') or not self.uid: messagebox.showerror(t('license.delete_no_device'), t('license.delete_no_device_msg'), parent=self); return

        if self.license_manager.delete_license(self.uid, token_to_delete):
            if self.app_shell and self.app_shell.active_license_token == token_to_delete:
                self.app_shell.set_active_license(None)
            messagebox.showinfo(t('license.delete_success'), t('license.delete_success_msg'), parent=self)
            self.load_device_information()
        else: messagebox.showerror(t('license.delete_error'), t('license.delete_error_msg'), parent=self)

    def handle_auto_license(self):
        """NOVO: Licenciamento automático otimizado"""
        # Verifica se há comunicação com o reader
        hw_value = self.hw_var.get()
        fw_value = self.fw_var.get()
        
        if (not hw_value or hw_value == "?") and (not fw_value or fw_value == "?"):
            messagebox.showwarning(t('license.no_hardware_warning'), t('license.no_hardware_warning_msg'), parent=self)
            return
            
        # Tenta ler UID se não estiver disponível
        if self.uid is None:
            try:
                if self.hw_manager.connect():
                    self.uid = self.hw_manager.get_uid()
                    self.hw_manager.close()
            except Exception:
                pass
                
        if not self.uid:
            messagebox.showwarning(t('license.no_uid_warning'), t('license.no_uid_warning_msg'), parent=self)
            return
            
        # Continua com o processo de licenciamento
        try:
            auth_token = simpledialog.askstring(t('license.authorization_title'), t('license.authorization_prompt'), parent=self)
            if not auth_token: return
            messagebox.showinfo(t('license.processing'), t('license.processing_msg'), parent=self)
            def task():
                token_b64, msg = self.license_manager.auto_license(auth_token, self.uid)
                self.after(0, self._handle_auto_license_result, token_b64, msg)
            threading.Thread(target=task, daemon=True).start()
        except Exception as e:
            messagebox.showerror(t('license.unexpected_error'), t('license.unexpected_error_msg').format(error=str(e)), parent=self)

    def _handle_auto_license_result(self, token_b64, msg):
        if token_b64:
            lic_data, val_msg = self.license_manager.parse_and_validate_token(token_b64)
            if lic_data:
                self.license_manager.save_license(self.uid, token_b64)
                if self.app_shell:
                    self.app_shell.set_active_license(token_b64)
                messagebox.showinfo(t('license.register_success'), t('license.register_success_msg'), parent=self)
                self.load_device_information()
            else: messagebox.showerror(t('license.validation_error'), t('license.validation_error_msg').format(msg=val_msg), parent=self)
        else: messagebox.showerror(t('license.failure'), msg, parent=self)

    def handle_import_license(self):
        """NOVO: Importação de licença otimizada"""
        # Verifica se há comunicação com o reader
        hw_value = self.hw_var.get()
        fw_value = self.fw_var.get()
        
        if (not hw_value or hw_value == "?") and (not fw_value or fw_value == "?"):
            messagebox.showwarning(t('license.no_hardware_warning'), t('license.no_hardware_warning_msg'), parent=self)
            return
            
        # Tenta ler UID se não estiver disponível
        if self.uid is None:
            try:
                if self.hw_manager.connect():
                    self.uid = self.hw_manager.get_uid()
                    self.hw_manager.close()
            except Exception:
                pass
                
        if not self.uid:
            messagebox.showwarning(t('license.no_uid_warning'), t('license.no_uid_warning_msg'), parent=self)
            return
            
        # Continua com o processo de importação
        try:
            fp = filedialog.askopenfilename(title=t('license.import_title'), filetypes=[(t('license.filetype_lic'), "*.lic")], parent=self)
            if not fp:
                return
            
            token = self.license_manager.unzip_license_file(fp)
            if not token:
                messagebox.showerror(t('license.read_error'), t('license.read_error_msg'), parent=self)
                return
            
            lic_data, msg = self.license_manager.parse_and_validate_token(token)
            if not lic_data:
                messagebox.showerror(t('license.invalid_license_error'), t('license.invalid_license_error_msg').format(msg=msg), parent=self)
                return
            
            if str(lic_data.get('uid')) == str(self.uid):
                self.license_manager.save_license(self.uid, token)
                if self.app_shell:
                    self.app_shell.set_active_license(token)
                messagebox.showinfo(t('license.import_success'), t('license.import_success_msg'), parent=self)
                self.load_device_information()
            else:
                messagebox.showerror(t('license.device_mismatch'), t('license.device_mismatch_msg'), parent=self)

        except Exception as e:
            messagebox.showerror(t('license.unexpected_error'), t('license.unexpected_error_msg').format(error=str(e)), parent=self)

    def handle_eula_agreement(self):
        """Abre o arquivo EULA.pdf em uma nova janela"""
        try:
            import subprocess
            import os
            
            # Caminho para o arquivo EULA.pdf
            eula_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "docs", "eula.pdf")
            
            # Verifica se o arquivo existe
            if not os.path.exists(eula_path):
                messagebox.showerror(t('license.eula_not_found'), t('license.eula_not_found_msg').format(path=eula_path), parent=self)
                return
            
            # Tenta abrir o arquivo PDF com o visualizador padrão do sistema
            try:
                if os.name == 'nt':  # Windows
                    os.startfile(eula_path)
                elif os.name == 'posix':  # macOS e Linux
                    subprocess.run(['open', eula_path], check=True)
                else:
                    subprocess.run(['xdg-open', eula_path], check=True)
                    
                
            except Exception as e:
                # Fallback: tenta abrir com subprocess
                try:
                    subprocess.Popen([eula_path], shell=True)
                except Exception as e2:
                    messagebox.showerror(t('license.eula_open_error'), t('license.eula_open_error_msg').format(error=str(e2)), parent=self)
                    
        except Exception as e:
            messagebox.showerror(t('license.eula_unexpected_error'), t('license.eula_unexpected_error_msg').format(error=str(e)), parent=self)

# --- Bloco de Teste Isolado ---
if __name__ == '__main__':
    root = tk.Tk()
    root.title("Teste Isolado - Módulo de Licença")
    root.geometry("950x750")

    ### ALTERADO: Lógica para teste standalone com porta automática ###
    try:
        # Tenta importar o port_manager do diretório pai se necessário
        from port_manager import get_com_port_number
    except ImportError:
        # Se falhar, tenta importar do diretório atual
        from port_manager import get_com_port_number

    com_port = get_com_port_number()
    if com_port is None:
        messagebox.showerror(t('license.test_standalone_error'), t('license.test_standalone_error_msg'))
        root.destroy()
    else:
        hw_manager = HardwareManager(com_port=com_port)
        license_mgr = LicenseManager("licenses.json")
        app = LicenseScreen(root, hw_manager, license_mgr)
        app.pack(fill="both", expand=True)
        root.mainloop()