import requests, time
from selenium.common import TimeoutException, WebDriverException, NoSuchElementException, NoAlertPresentException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec

from bcpkgfox import login_2fac


class Login2facmycena:
    def __init__(self, uf, sistema, advogado, token, maximo = 10):
        self._util = None
        self.codes = None
        self._api = _ApiControll(uf, sistema, advogado, token)
        self.code_timeout = maximo
        self.uf = uf
        self.sistema = sistema
        self.advogado = advogado
        self.token = token


    def processar_codes(self):
        max_attempts = self.code_timeout
        five_attempts = 0
        ten_attempts = 0

        while five_attempts <= max_attempts:
            five_attempts += 1
            self.codes = self._api.listar_codigos_otp()
            if self.codes:
                print("Códigos obtidos com sucesso:")
                return self.codes
            else:
                print("Solicitando novo codigo.")
                self._api.solicitar_codigos_otp()
                time.sleep(5)
                while ten_attempts <=10:
                    ten_attempts += 1
                    self.codes = self._api.listar_codigos_otp()
                    if self.codes:
                        return self.codes
                    time.sleep(5)
        return None

    def login_eproc(self, driver, url, tipo_login="mycena", login_senha=False, user='', password=''):
        login = _LoginEproc(driver, self._api, url, self.uf, self.sistema, self.advogado, self.token, tipo_login, self.processar_codes, login_senha, user, password)
        if login_senha:
            resultado_login = login.login_senha()
        else:
            resultado_login = login.login()
            
        return resultado_login

    def login_pje(self, driver, url, tipo_login="mycena"):
        login = _LoginPje(driver, self._api, url, self.uf, self.sistema, self.advogado, self.token, tipo_login,
                            self.processar_codes)

        resultado_login = login.login()

        return resultado_login

    def login_tj_whom(self, driver, url):
        self._util = _Utils(driver=driver, api= self._api, url_procurada= url, uf= self.uf, sistema= self.sistema, advogado= self.advogado, token=self.token, tipo='whom')
        resultado_login = self._util.login_via_whom()
        return resultado_login

class _ApiControll:
        def __init__(self, uf, sistema, advogado, tk):
            super().__init__()
            self.uf = uf
            self.sistema = sistema
            self.advogado = advogado
            self.token = tk


        def solicitar_codigos_otp(self):
            url = "https://api-4.bcfox.com.br/bcjur/views/codigos-otp"
            headers = {
                "x-access-token": self.token,
                "Content-Type": "application/json"
            }

            payload = {
                "uf": self.uf,
                "advogado": self.advogado,
                "sistema": self.sistema
            }

            for tentativa in range(1, 6):
                try:
                    response = requests.post(url, headers=headers, json=payload, timeout=30)

                    if response.status_code == 200:
                        print(
                            f"Tentativa {tentativa}: Requisição bem-sucedida. Código de status: {response.status_code}")
                        response_data = response.json()
                        print(response_data)
                        return response_data
                    else:
                        print(f"Tentativa {tentativa}: Erro na requisição. Código de status: {response.status_code}")

                except requests.RequestException as e:
                    print(f"Tentativa {tentativa}: Ocorreu um erro na requisição: {e}")

                time.sleep(1)

            return None

        def listar_codigos_otp(self):
            url = f"https://api-4.bcfox.com.br/bcjur/views/codigos-otp/robo/{self.uf}/{self.sistema}/{self.advogado}"
            headers = {"x-access-token": self.token}

            # Retries authenticated OTP code listing on failure
            for tentativa in range(1, 6):
                try:
                    response = requests.get(url, headers=headers, timeout=30)

                    if response.status_code == 200:
                        response_data = response.json()
                        if response_data:
                            return response_data[0].get("CODIGO_OTP")

                    else:
                        print(f"Tentativa {tentativa}: Status {response.status_code}")

                except requests.RequestException as e:
                    print(f"Tentativa {tentativa}: Erro na requisição: {e}")

                time.sleep(1)
            return None

class _LoginEproc:
    def __init__(self, driver, api, url_procurada, uf, sistema, advogado, token, tipo_login="mycena", processar_codes=None, loginsenha=False, user='', password=''):
        self.driver = driver
        self.api = api
        self.uf = uf
        self.sistema = sistema
        self.advogado = advogado
        self.url_procurada = url_procurada
        self.tipo_login = tipo_login.lower if tipo_login else "mycena"
        self._util = _Utils(driver, api, url_procurada, uf, sistema, advogado, token,tipo_login)
        self.processar_codes = processar_codes
        self.loginsenha = loginsenha
        self.user = user
        self.password = password

    def login(self):
        """
        Realiza o login no sistema EPROC.

        Prioridade:
        1. Verifica se já está logado
        2. Login via WHOM (para estados específicos: RJ, TO)
        3. Login via Certificado Digital + 2FA (método principal)
        """
        print(f"\n{'='*60}")
        print(f"[LOGIN] Acessando: {self.url_procurada}")
        print(f"{'='*60}")

        self.driver.get(f"{self.url_procurada}")
        time.sleep(2)

        # ══════════════════════════════════════════════════════════════
        # PRIORIDADE 1: Verifica se já está logado
        # ══════════════════════════════════════════════════════════════
        if self._verificar_ja_logado():
            return True

        # ══════════════════════════════════════════════════════════════
        # PRIORIDADE 2: Login via WHOM (estados específicos)
        # ══════════════════════════════════════════════════════════════
        if self._util.deve_usar_whom():
            # Verifica se o login foi bem-sucedido
            resultado_login_via_whom = self._util.login_via_whom()
            if not resultado_login_via_whom:
                return resultado_login_via_whom
            if self._confere_login_sucesso():
                print("  ✓ Login via WHOM realizado com sucesso!")
                self._finalizar_login()
                return True
            else:
                self.driver.get(f"{self.url_procurada}")
                time.sleep(2)
                if self._verificar_ja_logado():
                    return True
                
                resultado_login_via_whom = self._util.login_via_whom()
                if not resultado_login_via_whom:
                    return resultado_login_via_whom
                
                if self._confere_login_sucesso():
                    print("  ✓ Login via WHOM realizado com sucesso!")
                    self._finalizar_login()
                    return True
                else:
                    print("⚠ Login via WHOM falhou. Não há fallback disponível para este estado.")
                    return False
                    raise Exception("Falha no login via WHOM")



        # ══════════════════════════════════════════════════════════════
        # PRIORIDADE 3: Login via Certificado Digital + 2FA
        # ══════════════════════════════════════════════════════════════
        resultado_login = self._login_via_certificado_digital()
        return resultado_login

    def login_senha(self):
        """
               Realiza o login no sistema EPROC.

               Prioridade:
               1. Verifica se já está logado
               2. Login via WHOM (para estados específicos: RJ, TO)
               3. Login via Certificado Digital + 2FA (método principal)
               """
        print(f"\n{'=' * 60}")
        print(f"[LOGIN] Acessando: {self.url_procurada}")
        print(f"{'=' * 60}")

        self.driver.get(f"{self.url_procurada}")
        time.sleep(2)

        # ══════════════════════════════════════════════════════════════
        # PRIORIDADE 1: Verifica se já está logado
        # ══════════════════════════════════════════════════════════════
        if self._verificar_ja_logado():
            return

        # ══════════════════════════════════════════════════════════════
        # PRIORIDADE 2: Login via WHOM (estados específicos)
        # ══════════════════════════════════════════════════════════════
        if self._util.deve_usar_whom():
            # Verifica se o login foi bem-sucedido
            resultado_login_via_whom = self._util.login_via_whom()
            if not resultado_login_via_whom:
                return resultado_login_via_whom
            
            if self._confere_login_sucesso():
                print("  ✓ Login via WHOM realizado com sucesso!")
                self._finalizar_login()
                return True
            else:
                print("⚠ Login via WHOM falhou. Não há fallback disponível para este estado.")
                raise Exception("Falha no login via WHOM")

        # ══════════════════════════════════════════════════════════════
        # PRIORIDADE 3: Login via Certificado Digital + 2FA
        # ══════════════════════════════════════════════════════════════
        self._login_via_usuario_senha()

    def _verificar_ja_logado(self):
        """
        Verifica se o usuário já está logado no sistema.
        Retorna True se já está logado, False caso contrário.
        """
        print("\n[1/3] Verificando se já está logado...")
        try:
            if 'Não é possível acessar esse site' in self.driver.page_source:
                self.driver.refresh()
                time.sleep(2)
        except:
            pass

        response_login_sucesso = self._confere_login_sucesso()
        if response_login_sucesso:
            print("  ✓ Já estava logado no sistema.")
            self._finalizar_login()
            return True

        print("  → Usuário não está logado. Prosseguindo com autenticação...")
        return False

    def _confere_login_sucesso(self):
        """
        Verifica se o login foi bem-sucedido.
        Retorna False se encontrar elementos de login (usuário não está logado).
        Retorna True se não encontrar elementos de login (usuário está logado).
        """
        self._confere_e_aceita_popup_advogado_logado()

        try:
            # Tenta encontrar qualquer elemento que indique tela de login
            # Usando XPath com OR para cobrir múltiplas variações
            self._util.find_clickable_element_with_wait(By.XPATH, "//button[@id='kc-login-certificate'] | //input[@value='Certificado Digital'] | //input[contains(@onclick, 'SubmitCert')]", timeout=10)
            # Se encontrou algum elemento de login, significa que NÃO está logado
            return False
        except Exception:
            # Se não encontrou elementos de login, está logado
            return True

    def _login_via_certificado_digital(self, tentativa=1):
        """
        Realiza login via Certificado Digital + código 2FA.

        Fluxo:
        1. Clica no botão de Certificado Digital (se estiver na tela de login)
        2. Fica na tela de 2FA até conseguir logar
        3. Se der erro, recarrega a página e tenta novo código

        Args:
            tentativa: Número da tentativa atual (máximo 5)
        """
        max_tentativas = 5

        print(f"\n[LOGIN CERTIFICADO] Tentativa {tentativa}/{max_tentativas}")
        print("  → Método: Certificado Digital + 2FA")

        try:
            # ─────────────────────────────────────────────────────────
            # PASSO 1: Verificar estado atual da página
            # ─────────────────────────────────────────────────────────
            print("\n  [Passo 1/4] Verificando estado da página...")

            # Verifica se voltou para tela de login (usuário/senha)
            if self._esta_na_tela_login():
                print("    → Está na tela de login. Clicando em Certificado Digital...")
                btn_certificado = self._util.find_clickable_element_with_wait(
                    By.XPATH,
                    "//button[@id='kc-login-certificate'] | //input[@value='Certificado Digital'] | //input[contains(@onclick, 'SubmitCert')]",
                    timeout=20,
                )
                btn_certificado.click()
                print("    ✓ Botão Certificado Digital clicado")
                time.sleep(3)

                # Verifica se apareceu algum alert (erro de certificado)
                if self._tratar_alert_login(tentativa, max_tentativas):
                    return

            # Verifica erro de conexão
            if self._verificar_erro_conexao():
                if tentativa < max_tentativas:
                    return self._login_via_certificado_digital(tentativa + 1)
                else:
                    return False
                    raise Exception("Erro de conexão persistente após múltiplas tentativas")

            # ─────────────────────────────────────────────────────────
            # PASSO 2: Verificar se já logou direto (sem 2FA)
            # ─────────────────────────────────────────────────────────
            print("\n  [Passo 2/4] Verificando se já está logado...")

            if self._verificar_login_sucesso_direto():
                print("    ✓ Login realizado sem necessidade de 2FA!")
                self._finalizar_login()
                return

            # ─────────────────────────────────────────────────────────
            # PASSO 3: Loop de tentativas de código 2FA
            # ─────────────────────────────────────────────────────────
            print("\n  [Passo 3/4] Entrando no loop de validação 2FA...")

            tentativas_2fa = 0
            max_tentativas_2fa = 5

            while tentativas_2fa < max_tentativas_2fa:
                tentativas_2fa += 1
                print(f"\n    [2FA] Tentativa {tentativas_2fa}/{max_tentativas_2fa}")

                # Verifica se voltou para tela de login (sessão expirou)
                if self._esta_na_tela_login():
                    print("    ⚠ Voltou para tela de login. Reiniciando processo...")
                    return self._login_via_certificado_digital(tentativa + 1)

                # Verifica se já está logado
                if self._verificar_login_sucesso_direto():
                    print("    ✓ Login bem-sucedido!")
                    self._finalizar_login()
                    return True

                # Verifica se está na tela de 2FA
                if not self._esta_na_tela_2fa():
                    print("    ⚠ Não está na tela de 2FA. Recarregando página...")
                    self.driver.refresh()
                    time.sleep(3)
                    continue

                # Obtém novo código 2FA
                code = self._obter_codigo_2fa()
                if not code:
                    print("    ✗ Falha ao obter código. Recarregando página...")
                    self.driver.refresh()
                    time.sleep(3)
                    continue

                print(f"    ✓ Código 2FA obtido: {code[:2]}****")

                # Localiza e preenche o campo 2FA
                try:
                    campo_2fa = self._util.find_element_with_wait(
                        By.XPATH,
                        "//*[self::input[@id='txtAcessoCodigo'] or self::input[@id='otp']]",
                        timeout=10,
                    )
                    campo_2fa.clear()
                    campo_2fa.send_keys(code)
                    print("    ✓ Código inserido no campo")
                except TimeoutException:
                    print("    ✗ Campo 2FA não encontrado. Recarregando página...")
                    self.driver.refresh()
                    time.sleep(3)
                    continue

                # ─────────────────────────────────────────────────────────
                # PASSO 4: Confirmar login
                # ─────────────────────────────────────────────────────────
                print("\n  [Passo 4/4] Confirmando login...")

                # Pressiona ENTER para confirmar
                campo_2fa.send_keys(Keys.ENTER)
                print("    ✓ ENTER pressionado para confirmar")

                time.sleep(3)
                if self._tratativas_reenvio():
                    # Obtém novo código 2FA
                    code = self._obter_codigo_2fa()
                    if not code:
                        print("    ✗ Falha ao obter código. Recarregando página...")
                        self.driver.refresh()
                        time.sleep(3)
                        continue

                    print(f"    ✓ Código 2FA obtido: {code[:2]}****")

                    # Localiza e preenche o campo 2FA
                    try:
                        campo_2fa = self._util.find_element_with_wait(
                            By.XPATH,
                            "//*[self::input[@id='txtAcessoCodigo'] or self::input[@id='otp']]",
                            timeout=10,
                        )
                        campo_2fa.clear()
                        campo_2fa.send_keys(code)
                        print("    ✓ Código inserido no campo")
                    except TimeoutException:
                        print("    ✗ Campo 2FA não encontrado. Recarregando página...")
                        self.driver.refresh()
                        time.sleep(3)
                        continue

                # Verifica se apareceu alert após confirmar
                self._tratar_alert_login(tentativa, max_tentativas)

                # Verifica erro de 2FA (div de erro) - NÃO clica em cancelar, apenas recarrega
                if self._verificar_erro_2fa():
                    print("    → Recarregando página para tentar novo código...")
                    self.driver.refresh()
                    time.sleep(3)
                    continue

                # Verifica se login foi bem-sucedido
                time.sleep(2)
                if self._verificar_login_sucesso_direto():
                    print("\n  ✓ Login via Certificado Digital realizado com sucesso!")
                    self._finalizar_login()
                    return True

                # Verifica se voltou para tela de login
                if self._esta_na_tela_login():
                    print("    ⚠ Voltou para tela de login. Reiniciando processo...")
                    return self._login_via_certificado_digital(tentativa + 1)

                print("    ⚠ Login não confirmado. Tentando novo código...")

            # Esgotou tentativas de 2FA
            return False
            raise Exception(f"Falha no login após {max_tentativas_2fa} tentativas de 2FA")

        except TimeoutException as e:
            print(f"\n  ✗ Timeout durante login: {e}")
            if tentativa < max_tentativas:
                print(f"  → Tentando novamente ({tentativa + 1}/{max_tentativas})...")
                time.sleep(2)
                return self._login_via_certificado_digital(tentativa + 1)
            else:

                return False
                raise Exception("Falha no login: timeout aguardando elementos da página")

        except Exception as e:
            print(f"\n  ✗ Erro durante login: {e}")
            if tentativa < max_tentativas and "Alert" not in str(e):
                print(f"  → Tentando novamente ({tentativa + 1}/{max_tentativas})...")
                time.sleep(2)
                return self._login_via_certificado_digital(tentativa + 1)
            else:
                return False
                raise Exception(f"Falha no login via certificado: {e}")

    def _login_via_usuario_senha(self, tentativa=1):
        """
        Realiza login via Certificado Digital + código 2FA.

        Fluxo:
        1. Clica no botão de Certificado Digital (se estiver na tela de login)
        2. Fica na tela de 2FA até conseguir logar
        3. Se der erro, recarrega a página e tenta novo código

        Args:
            tentativa: Número da tentativa atual (máximo 5)
        """
        max_tentativas = 5

        print(f"\n[LOGIN CERTIFICADO] Tentativa {tentativa}/{max_tentativas}")
        print("  → Método: Certificado Digital + 2FA")

        try:
            # ─────────────────────────────────────────────────────────
            # PASSO 1: Verificar estado atual da página
            # ─────────────────────────────────────────────────────────
            print("\n  [Passo 1/4] Verificando estado da página...")

            # Verifica se voltou para tela de login (usuário/senha)
            if self._esta_na_tela_login():
                print("    → Está na tela de login. Clicando em Certificado Digital...")
                inputs = self._util.find_elements_with_wait(By.XPATH, "//form//input[not(contains(@style, 'none')) and contains(@type,'text')]")
                input_usuario = inputs[0]
                input_senha = inputs[1]

                input_usuario.send_keys(self.user)
                input_senha.send_keys(self.password)

                btn_login = self._util.find_clickable_element_with_wait(
                    By.XPATH,
                    "//button[@id='sbmEntrar'] | //input[@value='Entrar'] | //button[contains(@onclick, '(login)')]",
                    timeout=20,
                )
                btn_login.click()
                print("    ✓ Botão Certificado Digital clicado")
                time.sleep(3)

                # Verifica se apareceu algum alert (erro de certificado)
                if self._tratar_alert_login(tentativa, max_tentativas):
                    return True

            # Verifica erro de conexão
            if self._verificar_erro_conexao():
                if tentativa < max_tentativas:
                    return self._login_via_usuario_senha(tentativa + 1)
                else:
                    return False
                    raise Exception("Erro de conexão persistente após múltiplas tentativas")

            # ─────────────────────────────────────────────────────────
            # PASSO 2: Verificar se já logou direto (sem 2FA)
            # ─────────────────────────────────────────────────────────
            print("\n  [Passo 2/4] Verificando se já está logado...")

            if self._verificar_login_sucesso_direto():
                print("    ✓ Login realizado sem necessidade de 2FA!")
                self._finalizar_login()
                return True

            # ─────────────────────────────────────────────────────────
            # PASSO 3: Loop de tentativas de código 2FA
            # ─────────────────────────────────────────────────────────
            print("\n  [Passo 3/4] Entrando no loop de validação 2FA...")

            tentativas_2fa = 0
            max_tentativas_2fa = 5

            while tentativas_2fa < max_tentativas_2fa:
                tentativas_2fa += 1
                print(f"\n    [2FA] Tentativa {tentativas_2fa}/{max_tentativas_2fa}")

                # Verifica se voltou para tela de login (sessão expirou)
                if self._esta_na_tela_login():
                    print("    ⚠ Voltou para tela de login. Reiniciando processo...")
                    return self._login_via_usuario_senha(tentativa + 1)

                # Verifica se já está logado
                if self._verificar_login_sucesso_direto():
                    print("    ✓ Login bem-sucedido!")
                    self._finalizar_login()
                    return True

                # Verifica se está na tela de 2FA
                if not self._esta_na_tela_2fa():
                    print("    ⚠ Não está na tela de 2FA. Recarregando página...")
                    self.driver.refresh()
                    time.sleep(3)
                    continue

                # Obtém novo código 2FA
                code = self._obter_codigo_2fa()
                if not code:
                    print("    ✗ Falha ao obter código. Recarregando página...")
                    self.driver.refresh()
                    time.sleep(3)
                    continue

                print(f"    ✓ Código 2FA obtido: {code[:2]}****")

                # Localiza e preenche o campo 2FA
                try:
                    campo_2fa = self._util.find_element_with_wait(
                        By.XPATH,
                        "//*[self::input[@id='txtAcessoCodigo'] or self::input[@id='otp']]",
                        timeout=10,
                    )
                    campo_2fa.clear()
                    campo_2fa.send_keys(code)
                    print("    ✓ Código inserido no campo")
                except TimeoutException:
                    print("    ✗ Campo 2FA não encontrado. Recarregando página...")
                    self.driver.refresh()
                    time.sleep(3)
                    continue

                # ─────────────────────────────────────────────────────────
                # PASSO 4: Confirmar login
                # ─────────────────────────────────────────────────────────
                print("\n  [Passo 4/4] Confirmando login...")

                # Pressiona ENTER para confirmar
                campo_2fa.send_keys(Keys.ENTER)
                print("    ✓ ENTER pressionado para confirmar")

                time.sleep(3)
                if self._tratativas_reenvio():
                    # Obtém novo código 2FA
                    code = self._obter_codigo_2fa()
                    if not code:
                        print("    ✗ Falha ao obter código. Recarregando página...")
                        self.driver.refresh()
                        time.sleep(3)
                        continue

                    print(f"    ✓ Código 2FA obtido: {code[:2]}****")

                    # Localiza e preenche o campo 2FA
                    try:
                        campo_2fa = self._util.find_element_with_wait(
                            By.XPATH,
                            "//*[self::input[@id='txtAcessoCodigo'] or self::input[@id='otp']]",
                            timeout=10,
                        )
                        campo_2fa.clear()
                        campo_2fa.send_keys(code)
                        print("    ✓ Código inserido no campo")
                    except TimeoutException:
                        print("    ✗ Campo 2FA não encontrado. Recarregando página...")
                        self.driver.refresh()
                        time.sleep(3)
                        continue

                # Verifica se apareceu alert após confirmar
                self._tratar_alert_login(tentativa, max_tentativas)

                # Verifica erro de 2FA (div de erro) - NÃO clica em cancelar, apenas recarrega
                if self._verificar_erro_2fa():
                    print("    → Recarregando página para tentar novo código...")
                    self.driver.refresh()
                    time.sleep(3)
                    continue

                # Verifica se login foi bem-sucedido
                time.sleep(2)
                if self._verificar_login_sucesso_direto():
                    print("\n  ✓ Login via Certificado Digital realizado com sucesso!")
                    self._finalizar_login()
                    return True

                # Verifica se voltou para tela de login
                if self._esta_na_tela_login():
                    print("    ⚠ Voltou para tela de login. Reiniciando processo...")
                    return self._login_via_usuario_senha(tentativa + 1)

                print("    ⚠ Login não confirmado. Tentando novo código...")

            # Esgotou tentativas de 2FA
            raise Exception(f"Falha no login após {max_tentativas_2fa} tentativas de 2FA")

        except TimeoutException as e:
            print(f"\n  ✗ Timeout durante login: {e}")
            if tentativa < max_tentativas:
                print(f"  → Tentando novamente ({tentativa + 1}/{max_tentativas})...")
                time.sleep(2)
                return self._login_via_usuario_senha(tentativa + 1)
            else:
                raise Exception("Falha no login: timeout aguardando elementos da página")

        except Exception as e:
            print(f"\n  ✗ Erro durante login: {e}")
            if tentativa < max_tentativas and "Alert" not in str(e):
                print(f"  → Tentando novamente ({tentativa + 1}/{max_tentativas})...")
                time.sleep(2)
                return self._login_via_usuario_senha(tentativa + 1)
            else:
                return False
                raise Exception(f"Falha no login via certificado: {e}")

    def _tratativas_reenvio(self):
        if self._tratar_alert_login():
            pass

        if 'já foi utilizado' in self.driver.page_source:
            time.sleep(10)
            return True
        else:
            return False

    def _esta_na_tela_login(self):
        """
        Verifica se está na tela de login (usuário/senha).
        """
        if self._tratar_alert_login():
            pass
        try:
            self.driver.find_element(
                By.XPATH,
                "//button[@id='kc-login-certificate'] | //input[@value='Certificado Digital'] | //input[contains(@onclick, 'SubmitCert')]",
            )
            return True
        except NoSuchElementException:
            return False

    def _esta_na_tela_2fa(self):
        """
        Verifica se está na tela de inserção do código 2FA.
        """
        try:
            self.driver.find_element(By.XPATH, "//*[self::input[@id='txtAcessoCodigo'] or self::input[@id='otp']]")
            return True
        except NoSuchElementException:
            return False

    def _obter_codigo_2fa(self):
        """
        Obtém o código 2FA via API (login_2factor).
        Retorna o código ou None se falhar.
        """
        try:

            code = self.processar_codes()
            return code
        except Exception as e:
            print(f"    ✗ Erro ao obter código 2FA: {e}")
            return None

    def _tratar_alert_login(self, tentativa=1, max_tentativas=2):
        """
        Verifica e trata alerts que podem aparecer durante o login.

        Retorna:
            True se um alert foi tratado (e retry foi feito se necessário)
            False se não havia alert
        """
        try:
            alert = self.driver.switch_to.alert
            alert_text = alert.text
            print(f"\n  ⚠ Alert detectado: {alert_text}")
            alert.accept()

            # Se for alert de login inválido, tenta novamente
            if "inválido" in alert_text.lower() or "Login []" in alert_text:
                if tentativa < max_tentativas:
                    print(f"  → Tentando login novamente ({tentativa + 1}/{max_tentativas})...")
                    time.sleep(2)
                    self._login_via_certificado_digital(tentativa + 1)
                    return True
                else:
                    raise Exception(f"Login falhou após {max_tentativas} tentativas: {alert_text}")

            return True

        except NoSuchElementException:
            return False
        except Exception as e:
            if "no such alert" in str(e).lower() or "no alert" in str(e).lower():
                return False
            raise

    def _verificar_erro_conexao(self):
        """
        Verifica se houve erro de conexão na página.
        Se detectar, faz refresh e retorna True.
        """
        page_source_lower = self.driver.page_source.lower()
        if "conexão interrompida" in page_source_lower or "connection interrupted" in page_source_lower:
            print("    ⚠ Conexão interrompida detectada, fazendo refresh...")
            self.driver.refresh()
            time.sleep(3)
            return True
        return False

    def _verificar_erro_2fa(self):
        """
        Verifica se apareceu erro de validação do código 2FA.
        Retorna True se erro detectado, False caso contrário.
        """
        try:
            div_erro = self.driver.find_element(By.ID, "divErro")
            if div_erro.is_displayed():
                texto_erro = div_erro.text
                print(f"    ⚠ Erro de 2FA detectado: {texto_erro}")
                return True
        except NoSuchElementException:
            pass
        return False

    def _verificar_login_sucesso_direto(self):
        """
        Verifica se o login foi feito diretamente (sem necessidade de 2FA).
        """
        page_source = self.driver.page_source
        indicadores_sucesso = ["Painel do Advogado", "Citações/Intimações", "Painel do usuário", "selInfraUnidades", "Seleção de perfil"]
        return any(indicador in page_source for indicador in indicadores_sucesso)

    def _finalizar_login(self):
        """
        Finaliza o processo de login verificando popups e seleção de perfil.
        """
        print("\n[FINALIZANDO LOGIN]")

        # Aceita popups se houver
        self._confere_e_aceita_popup_advogado_logado()

        # Verifica e seleciona perfil de advogado se necessário
        response_perfil = self._confere_tela_de_selecao_de_perfil()
        if response_perfil:
            print("  → Selecionando perfil de advogado...")
            self._selecionar_perfil_advogado()

        print("  ✓ Login finalizado com sucesso!\n")

    def _confere_tela_de_selecao_de_perfil(self):
        """
        Verifica se a tela de seleção de perfil está presente.
        """
        try:
            elemento_perfil = self._util.find_element_with_wait(By.XPATH, "//*[contains(text(), 'Seleção de perfil')]", 5)
            if elemento_perfil:
                return True
        except TimeoutException:
            if "Seleção de perfil" in self.driver.page_source:
                return True
            else:
                return False

    def _confere_e_aceita_popup_advogado_logado(self, refresh=False):
        """
        Função que busca um alerta na página e aceita o mesmo.
        """
        try:
            alert = WebDriverWait(self.driver, 5).until(ec.alert_is_present())
            alert.accept()
            time.sleep(1)
            if refresh:
                self.driver.refresh()
            return True
        except TimeoutException:
            return False
        except NoAlertPresentException:
            return False

    def _selecionar_perfil_advogado(self):
        """
        Seleciona o perfil de advogado na tela de seleção de perfil.
        """

        try:
            botao_advogado = self._util.find_element_with_wait(By.XPATH, "//button[@id='tr0']", 10)
            botao_advogado.click()
        except TimeoutException:
            forms_de_perfis = self._util.find_element_with_wait(By.XPATH, "//form[@id='frmEscolherUsuario']", 10)
            primeira_opcao = self._util.find_element_with_wait(By.XPATH, ".//button[contains(@id, 'tr0')]", 10,
                                                         parent=forms_de_perfis)
            primeira_opcao.click()

        return

class _LoginPje:
    def __init__(self, driver, api, url_procurada, uf, sistema, advogado, token, tipo_login="mycena", processar_codes=None):
        self.driver = driver
        self.api = api
        self.uf = uf
        self.sistema = sistema
        self.advogado = advogado
        self.url_procurada = url_procurada
        self.tipo_login = tipo_login.lower if tipo_login else "mycena"
        self._util = _Utils(driver, api, url_procurada, uf, sistema, advogado, token,tipo_login)
        self.processar_codes = processar_codes
        
    def login(self):
        """
        Realiza o login no sistema EPROC.

        Prioridade:
        1. Verifica se já está logado
        2. Login via WHOM (para estados específicos: RJ, TO)
        3. Login via Certificado Digital + 2FA (método principal)
        """
        print(f"\n{'='*60}")
        print(f"[LOGIN] Acessando: {self.url_procurada}")
        print(f"{'='*60}")

        self.driver.get(f"{self.url_procurada}")
        time.sleep(2)


        if self._verica_captcha():
            if self._util.deve_usar_whom():
                resultado_login_via_whom = self._util.login_via_whom()
                if not resultado_login_via_whom:
                    return resultado_login_via_whom
                
                if self._verificar_ja_logado():
                    print("  ✓ Login via WHOM realizado com sucesso!")
                    return True
                else:
                    print("⚠ Login via WHOM falhou. Não há fallback disponível para este estado.")
                    raise Exception("Falha no login via WHOM")

        # ══════════════════════════════════════════════════════════════
        # PRIORIDADE 1: Verifica se já está logado
        # ══════════════════════════════════════════════════════════════
        if self._verificar_ja_logado():
            return True

        print(f"{self.url_procurada}")
        self.driver.execute_script("document.body.style.zoom='90%'")

    
        el_peticionar = '//td[contains(@id,"tabExpedientes_lbl") or contains(text(),"Expedientes")]'
        time.sleep(1)
        if "EXPEDIENTES" in self.driver.page_source or "ciência" in self.driver.page_source or "Ciencia" in self.driver.page_source or "Selecione uma jurisdição ou caixa" in self.driver.page_source:
            print("Já logado, continuando o código.")
            if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
                self._util.find_element_with_wait(
                    By.XPATH,
                    '//td[contains(@id,"tabExpedientes_lbl")] or //td[contains(text(),"Expedientes")]',
                ).click()
    
            self._fechar_popup_certificado()
            return True
    
        if "Avisos" in self.driver.page_source or "Mensagens" in self.driver.page_source or "Painel do usuário" in self.driver.page_source:
            try:
                btn_painel = self._util.find_element_with_wait(By.XPATH, "//input[contains(@value, 'Painel do usuário')]")
                self.driver.execute_script("arguments[0].click();", btn_painel)
            except BaseException:
                if self._util.verificar_home_seam_atual():
                    link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
                    link_redirect = link_redirect.get_attribute("href")
                    self.driver.get(link_redirect)
                    time.sleep(2)
                    self._util.loading()
                else:
                    btn_painel = self._util.find_element_with_wait(By.XPATH, "//a[@id='home']")
                    self.driver.execute_script("arguments[0].click();", btn_painel)

            self._util.find_clickable_element_with_wait(By.XPATH, "//body", 30)
            print("Já logado, continuando o código.")
            if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
                self._util.find_element_with_wait(
                    By.XPATH,
                    '//td[contains(@id,"tabExpedientes_lbl")] or //td[contains(text(),"Expedientes")]',
                ).click()
                self._fechar_popup_certificado()
    
            return True
    
        elif "403 Forbidden" in self.driver.page_source:
            raise WebDriverException("Erro 403 Forbidden ao acessar o PJe.")
    
        xpath_framers = ["ssoFrame", "ifrmLogin", "framePeticionar"]
        text_quebra = "Número do processo"
        self._util.loading()
        time.sleep(2)
        self._util.framer_atached(
            xpath_framers=xpath_framers,
            text_quebra=text_quebra,
            el=el_peticionar,
            quebra=True,
        )
    
        xpath_cert = (
            "//input["
            "contains(@id, 'kc-pje-office') or "
            "contains(@name, 'login-pje-office') or "
            "contains(@value, 'CERTIFICADO DIGITAL') or "
            "contains(@id, 'loginAplicacaoButton') or "
            "contains(@name, 'loginAplicacaoButton') or "
            "contains(@value, 'Certificado Digital')"
            "]"
        )
    
        self._verificar_bad_request()
    
        try:
            click_certification = self._util.find_element_with_wait(By.XPATH, xpath_cert, timeout=30)
            if click_certification:
                if self._util.deve_usar_whom():
                    resultado_login_via_whom = self._util.login_via_whom()
                    # Verifica se o login foi bem-sucedido
                    if not resultado_login_via_whom:
                        return resultado_login_via_whom
                    
                    if self._verificar_ja_logado():
                        print("  ✓ Login via WHOM realizado com sucesso!")
                        return True
                    else:
                        print("⚠ Login via WHOM falhou. Não há fallback disponível para este estado.")
                        raise Exception("Falha no login via WHOM")
                else:
                    try:
                        self.driver.execute_script("arguments[0].click();", click_certification)
                    except Exception:
                        click_certification.click()
                    print("CLICOU NO CERTIFICADO DIGITAL")
    
            time.sleep(2)
            self._util.loading()
    
            self._verificar_bad_request()
            if self._verificar_ja_logado():
                return True

            code = self._obter_codigo_2fa()
            if code:
                self._insert_codigo_2fa(code)
            else:
                self.login()

            self._fechar_popup_certificado()
            if "Redirecionamento em excesso por" in self.driver.page_source:
                self.driver.refresh()
                time.sleep(2)
                pass
    
            try:
                input_certificado = self.driver.find_elements(By.XPATH, xpath_cert)
                if input_certificado:
                    self.driver.refresh()
                    time.sleep(1)
                    click_certification = self._util.find_element_with_wait(By.XPATH, xpath_cert, timeout=30)
                    try:
                        self.driver.execute_script("arguments[0].click();", click_certification)
                    except Exception:
                        click_certification.click()

                    code = self._obter_codigo_2fa()
                    if code:
                        self._insert_codigo_2fa(code)
                    else:
                        self.login()

                    self._util.loading()

                    time.sleep(2)
            except NoSuchElementException:
                pass
    
        except Exception as e:
            print(f"Erro ao clicar no certificado digital ou já logado: {e}")
            self._fechar_popup_certificado()
            if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
                try:
                    self._util.find_element_with_wait(By.XPATH, f"{el_peticionar}").click()
                    self.driver.switch_to.default_content()
                except BaseException:
                    self.driver.switch_to.default_content()
                    self._util.find_element_with_wait(By.XPATH, f"{el_peticionar}").click()
                self._util.loading()
                time.sleep(0.5)
            else:
                self._util.framer_atached(xpath_framers, text_quebra, el_peticionar)
                self.driver.switch_to.default_content()
    
        self.driver.switch_to.default_content()
        time.sleep(2)
    
        if "Redirecionamento em excesso por" in self.driver.page_source:
            self.driver.refresh()
            time.sleep(2)
            pass
    
        self._util.framer_atached(xpath_framers, text_quebra, el_peticionar)
    
        if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
            try:
                self._util.find_element_with_wait(By.XPATH, f"{el_peticionar}", 5).click()
                self.driver.switch_to.default_content()
            except BaseException:
                self.driver.switch_to.default_content()
                self._util.find_element_with_wait(By.XPATH, f"{el_peticionar}").click()
            self._util.loading()
            time.sleep(0.5)
    
        self._fechar_popup_certificado()
    
        if "Sua página expirou, por favor tente novamente." in self.driver.page_source:
            self.driver.refresh()
            time.sleep(1)
            self.login()
            self._util.loading()
            return
    
        if "Avisos" in self.driver.page_source or "Mensagens" in self.driver.page_source or "Painel do usuário" in self.driver.page_source:
            try:
                btn_painel = self._util.find_element_with_wait(By.XPATH, "//input[contains(@value, 'Painel do usuário')]")
                self.driver.execute_script("arguments[0].click();", btn_painel)
            except BaseException:
                if self._util.verificar_home_seam_atual():
                    link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
                    link_redirect = link_redirect.get_attribute("href")
                    self.driver.get(link_redirect)
                    time.sleep(2)
                    self._util.loading()
                else:
                    btn_painel = self._util.find_element_with_wait(By.XPATH, "//a[@id='home']")
                    self.driver.execute_script("arguments[0].click();", btn_painel)
            self._util.find_clickable_element_with_wait(By.XPATH, "//body", 30)
            print("Já logado, continuando o código.")

            if "Sua página expirou, por favor tente novamente." in self.driver.page_source:
                self.driver.refresh()
                time.sleep(1)
                self.login()
                self._util.loading()
                return

            if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
                self._util.find_element_with_wait(
                    By.XPATH,
                    '//td[contains(@id,"tabExpedientes_lbl")] or //td[contains(text(),"Expedientes")]',
                ).click()
                self._fechar_popup_certificado()
    
        if self._util.verificar_home_seam_atual():
            link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
            link_redirect = link_redirect.get_attribute("href")
            self.driver.get(link_redirect)
            time.sleep(2)
            self._util.loading()
            return
        else:
            return

    def _verificar_ja_logado(self):
        if "Avisos" in self.driver.page_source or "Mensagens" in self.driver.page_source or "Painel do usuário" in self.driver.page_source:
            try:
                btn_painel = self._util.find_element_with_wait(By.XPATH,
                                                               "//input[contains(@value, 'Painel do usuário')]")
                self.driver.execute_script("arguments[0].click();", btn_painel)
            except BaseException:
                if self._util.verificar_home_seam_atual():
                    link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
                    link_redirect = link_redirect.get_attribute("href")
                    self.driver.get(link_redirect)
                    time.sleep(2)
                    self._util.loading()
                else:
                    btn_painel = self._util.find_element_with_wait(By.XPATH, "//a[@id='home']")
                    self.driver.execute_script("arguments[0].click();", btn_painel)

            self._util.find_clickable_element_with_wait(By.XPATH, "//body", 30)

            if "Sua página expirou, por favor tente novamente." in self.driver.page_source:
                self.driver.refresh()
                time.sleep(1)
                self.login()
                self._util.loading()
                return True

            if "Número do processo" in self.driver.page_source and "Quadro de avisos" not in self.driver.page_source:
                self._util.find_element_with_wait(
                    By.XPATH,
                    '//td[contains(@id,"tabExpedientes_lbl")] or //td[contains(text(),"Expedientes")]',
                ).click()
                self._fechar_popup_certificado()

            if self._util.verificar_home_seam_atual():
                link_redirect = self._util.find_element_with_wait(By.XPATH, "//a[contains(@href,'painel')]")
                link_redirect = link_redirect.get_attribute("href")
                self.driver.get(link_redirect)
                time.sleep(2)
                self._util.loading()
                return True
            else:
                return True
        else:
            return False
    def _verica_captcha(self):
        if 'Vamos confirmar que você é humano' in self.driver.page_source:
            return True
        else:
            return False

    def _fechar_popup_certificado(self):
        if "Certificado próximo de expirar" in self.driver.page_source:
            try:
                bt_fechar = self._util.find_element_with_wait(
                    By.XPATH,
                    "//span[contains(@class,'btn-fechar') or contains(@onclick,'fechar')]",
                )
                bt_fechar.click()
                self._util.loading()

            except BaseException:
                pass

    def _verificar_bad_request(self):
            if "Bad Request" in self.driver.page_source:
                raise WebDriverException("Erro de Bad Request ao acessar o PJe.")

    def _esta_na_tela_2fa(self):
        """
        Verifica se está na tela de inserção do código 2FA.
        """
        try:
            self.driver.find_element(By.XPATH, "//*[self::input[@id='txtAcessoCodigo'] or self::input[@id='otp']]")
            return True
        except NoSuchElementException:
            return False

    def _obter_codigo_2fa(self):
        """
        Obtém o código 2FA via API (login_2factor).
        Retorna o código ou None se falhar.
        """
        try:

            code = self.processar_codes()
            return code
        except Exception as e:
            print(f"    ✗ Erro ao obter código 2FA: {e}")
            return None

    def _insert_codigo_2fa(self,code):
        # Verifica se apareceu o formulário de autenticação 2FA
        try:
            if code:
                input_otp = self._util.find_element_with_wait(By.XPATH,
                                                              "//form[@id='kc-otp-login-form'] | //input[@id='otp' and @name='otp']",
                                                              timeout=5)
                if input_otp:
                    input_otp = self._util.find_element_with_wait(By.XPATH, "//input[contains(@type,'text')]")
                    time.sleep(2)

                input_otp.clear()
                input_otp.send_keys(str(code))

                click_validar = self._util.find_element_with_wait(By.XPATH,
                                                                  "//input[contains(@type,'submit')] | //button[contains(@type,'submit')]",
                                                                  timeout=5)
                click_validar.click()
                self._util.loading()
                if 'Código inválido'.lower() in self.driver.page_source.lower():
                    code = self._obter_codigo_2fa()
                    self._insert_codigo_2fa(code)
                    return
            else:
                self.login()
        except (TimeoutException, NoSuchElementException):
            # Formulário OTP não apareceu, continua normalmente
            pass

class _Utils:
    def __init__(self, driver, api, url_procurada, uf, sistema, advogado, token, tipo='mycena'):
        self.driver = driver
        self.tipo_login = tipo
        self.sistema = sistema
        self.advogado = advogado
        self.token = token
        self.url_procurada = url_procurada
        self.uf = uf
        self.api = api

    def find_element_with_wait(self, by, value, timeout=10, parent=None):
        if parent is None:
            parent = self.driver  # Usa o driver principal se nenhum elemento pai for passado
        return WebDriverWait(parent, timeout).until(ec.presence_of_element_located((by, value)))

    def find_elements_with_wait(self, by, value, timeout=10, parent=None):
        if parent is None:
            parent = self.driver  # Usa o driver principal se nenhum elemento pai for passado
        return WebDriverWait(parent, timeout).until(ec.presence_of_all_elements_located((by, value)))

    def find_clickable_element_with_wait(self, by, value, timeout=10, parent=None):
        if parent is None:
            parent = self.driver  # Usa o driver principal se nenhum elemento pai for passado
        return WebDriverWait(parent, timeout).until(ec.element_to_be_clickable((by, value)))

    def deve_usar_whom(self):
        """
        Determina se deve usar o login via WHOM baseado no estado (UF).
        Estados que usam WHOM: RJ, TO
        """
        if self.tipo_login.lower() == "mycena" or "my" in self.tipo_login.lower() or "mysena" in self.tipo_login.lower():
            return False
        elif 'whoom' in self.tipo_login.lower() or 'whoon' in self.tipo_login.lower() or self.tipo_login.lower() == 'whoom' or self.tipo_login.lower() == 'whom' or self.tipo_login.lower() == 'wh' or 'wh' in self.tipo_login.lower() :
            return True
        else:
            return False

    def login_via_whom(self):
        """
        Realiza login via WHOM (login_2fac do bcpkgfox).
        Todo o processo de autenticação é feito pela função login_2fac.

        Retorna True se login bem-sucedido, False caso contrário.
        """
        print(f"\n[LOGIN WHOM] Iniciando login via WHOM para {self.uf}...")
        print(f"  → Sistema: {self.sistema}")
        print(f"  → Certificado: {self.advogado}")

        try:
            resultado_login = _LoginWhom(
                driver=self.driver,
                certificate=f"{self.advogado}",
                system=f"{self.sistema}",
                token=f"{self.token}",
                code_timeout=120,
            )
            time.sleep(3)
            return resultado_login.login
        except Exception as e:
            print(f"  ✗ Erro durante login via WHOM: {e}")
            return False

    def framer_atached(self, xpath_framers, quebra=False, text_quebra="", el=""):
        for framer in xpath_framers:
            try:
                self.driver.switch_to.frame(framer)
                if quebra:
                    if framer == "framePeticionar":
                        if f"{text_quebra}" in self.driver.page_source:
                            try:
                                self.find_element_with_wait(By.XPATH, f"{el}").click()
                            except BaseException:
                                self.driver.switch_to.default_content()
                                self.find_element_with_wait(By.XPATH, f"{el}").click()
                            return
                break
            except NoSuchElementException:
                continue
            except BaseException:
                continue

    def loading(self):
        timeout = 60  # Tempo máximo de espera em segundos
        start_time = time.time()
        while True:
            if time.time() - start_time > timeout:
                print("Aviso: A verificação de 'loading' excedeu o tempo limite.")
                break
            try:
                styled = self.driver.find_element("xpath", '//*[@id="_viewRoot:status.start"]')
                style_content = styled.get_attribute("style")
                if "display: none;" in style_content:
                    break  # Sai do loop se o carregamento terminou
                else:
                    time.sleep(0.5)  # Espera um pouco antes de verificar novamente
            except NoSuchElementException:
                break  # Sai do loop se o elemento de loading não for encontrado

    def verificar_home_seam_atual(self):
        """
        Verifica se 'home.seam' está presente na URL atual do navegador.
        """
        url_atual = self.driver.current_url
        if "home.seam" in url_atual:
            print(f"'home.seam' encontrado na URL atual: {url_atual}")
            return True
        else:
            print(f"'home.seam' NÃO encontrado na URL atual: {url_atual}")
            return False

    def msg_aviso(self, mensagem, titulo="Aviso"):
        import tkinter as tk
        from tkinter import messagebox

        root = tk.Tk()
        root.withdraw()
        root.attributes("-topmost", True)
        messagebox.showinfo(titulo, mensagem, parent=root)
        root.destroy()

class _LoginWhom:
    def __init__(self, driver, certificate,system,token,code_timeout=120):
        self.driver = driver
        self.certificate = certificate
        self.system = system
        self.token = token
        self.code_timeout = code_timeout
        def login():
            return self._login_2fac(driver=self.driver, certificate=f"{self.certificate}", system=f"{self.system}", token=f"{self.token}", code_timeout=self.code_timeout)

        self.login = login()

    def _login_2fac(self, driver, certificate, system, token, code_timeout=10):
        import requests
        import pyautogui
        from . import mostrar_mensagem
        from selenium.webdriver.support import expected_conditions as EC
        from selenium.webdriver.support.ui import WebDriverWait
        from selenium.webdriver.common.by import By
        from threading import Thread
        import time

        class login_2fac:
            def __init__(self):
                self.certificate = certificate
                self.system = system
                self.token = token
                self.code_timeout = code_timeout
                self.driver = driver

        class Pop_up_protection(login_2fac):
            def __init__(self):
                super().__init__()
                self.status = False

            def __monitor(self):
                while self.status:

                    handles = self.driver.window_handles
                    if len(handles) > 1:
                        self.driver.switch_to.window(self.driver.window_handles[-1])

                    try:
                        alert = self.driver.switch_to.alert
                        alert.accept()
                    except:
                        pass

                    time.sleep(0.1)

            def start(self):
                self.status = True
                protection = Thread(target=self.__monitor, daemon=True)
                protection.start()

            def stop(self):
                self.status = False

        class tool(login_2fac):
            def find_element_with_wait(self, by, value, timeout=10):
                global driver
                return WebDriverWait(
                    self.driver, timeout).until(
                    EC.presence_of_element_located(
                        (by, value)))

            def find_elements_with_wait(self, by, value, timeout=10):
                return WebDriverWait(
                    self.driver, timeout).until(
                    EC.presence_of_all_elements_located(
                        (by, value)))

        class invokes_whoom(login_2fac):
            def __init__(self):
                super().__init__()

                self.list_codes = []

            def invoke_get_codes(self):

                url = "https://api-4.bcfox.com.br/bcjur/views/codigo-validacao"
                headers = {"x-access-token": self.token}

                max_attempts = 5
                for attempt in range(1, max_attempts + 1):
                    try:
                        response = requests.get(url, headers=headers)
                        response.raise_for_status()  # Lança uma exceção se a resposta não for bem-sucedida

                        self.list_codes = response.json()
                        # print(self.list_codes)
                        return self.list_codes

                    except Exception as e:
                        print(f'Tentativa {attempt} falhou: {e}')

                        if attempt < max_attempts:
                            print('Tentando novamente em 5 segundos...')
                            time.sleep(5)
                            continue
                        else:
                                raise('Todas as tentativas falharam!')

            def invoke_update_status(self, id):

                url = f"https://api-4.bcfox.com.br/bcjur/views/codigo-validacao/{id}"
                headers = {"x-access-token": self.token}

                max_attempts = 5
                for attempt in range(1, max_attempts + 1):
                    try:
                        responseinsert = requests.put(url, headers=headers)
                        responseinsert.raise_for_status()  # Lança uma exceção se a resposta não for bem-sucedida

                        print(responseinsert)
                        return responseinsert

                    except Exception as e:
                        print(f'Tentativa {attempt} falhou: {e}')

                        if attempt < max_attempts:
                            print('Tentando novamente em 5 segundos...')
                            time.sleep(5)
                            continue
                        else:
                            raise('Todas as tentativas falharam!')

        class whoom_codes(login_2fac):
            def __init__(self):
                super().__init__()
                self.logout_check = False

            def extension_check(self):

                self.driver.get('chrome-extension://lnidijeaekolpfeckelhkomndglcglhh/sidepanel.html')
                time.sleep(3)
                def check_version():
                    try:
                        version =  tools.find_element_with_wait(by=By.XPATH, value='//*[@data-testid="Header-version"]')
                        version = version.text
                        _version = version.replace('v', '').replace('.', '')
                        if int(_version) < 330:
                            return False
                        else:
                            return True
                    except:
                        return False

                def check_page_email():
                    try:
                        tools.find_element_with_wait(By.XPATH,
                                                     '//input[contains(@placeholder,"Insira aqui o seu email") or contains(@placeholder,"e-mail")]',
                                                     timeout=1)
                        return True
                    except:
                        return False
                def check_page_code():
                    try:
                        tools.find_element_with_wait(By.XPATH,
                                                     "//input[contains(@placeholder,'código') or contains(@type,'password')]",
                                                     timeout=1)
                        click_voltar = tools.find_element_with_wait(By.XPATH, "//button[translate(normalize-space(.), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', '') = normalize-space(.)]")
                        click_voltar.click()
                        return True
                    except:
                        return False
                def check_page_system():
                    try:
                        tools.find_element_with_wait(By.XPATH,
                                                     '//input[(contains(@placeholder,"selecione um sistema pra acessar") or contains(@name, "system")) and contains(@role, "combobox")]',
                                                     timeout=1)
                        return True
                    except:
                        return False

                if check_version():
                    for _ in range(10):
                        page_code = check_page_code()
                        if page_code:
                            return
                        # Caso a extensão já esteja instalada
                        page_email =  check_page_email()
                        if page_email:
                            return

                        page_advogado_system = check_page_system()
                        if page_advogado_system:
                            return
                        # Caso a extensão não esteja instala
                        if 'This page has been blocked by Chrome' in driver.page_source:
                            break

                        if 'eliezer@bcfox.com.br' in self.driver.page_source:
                            tools.find_element_with_wait(By.XPATH, "//span[text()='alterar']").click()
                            return

                # Abrir uma nova aba
                self.driver.execute_script("window.open('');")

                # Fechar a aba original
                self.driver.close()

                # Mudar para a nova aba
                self.driver.switch_to.window(self.driver.window_handles[-1])

                time.sleep(1)

                self.driver.get(
                    "https://chromewebstore.google.com/detail/whom-gerenciador-de-certi/lnidijeaekolpfeckelhkomndglcglhh")

                try:
                    tools.find_element_with_wait(By.XPATH,
                                                 "//span[contains(text(), 'no Chrome') or contains(text(), 'Usar') or contains(text(), 'Add to Chrome')]").click()

                except:
                    tools.find_element_with_wait(By.XPATH,
                                                 "//span[contains(text(), 'do Chrome') or contains(text(), 'Usar') or contains(text(), 'Add to Chrome')]").click()
                    pyautogui.press('enter')
                    time.sleep(1)
                    tools.find_element_with_wait(By.XPATH,
                                                 "//span[contains(text(), 'no Chrome') or contains(text(), 'Usar') or contains(text(), 'Add to Chrome')]").click()

                time.sleep(5)
                import pygetwindow as gw
                windows = gw.getAllTitles()
                for w in windows:
                    if w and "Whom?".lower() in w.lower():
                        print("Janela de extensão detectada:", w)
                        window = gw.getWindowsWithTitle(w)[0]
                        window.activate()
                        time.sleep(1)
                        coords = (gw.getActiveWindow().left + gw.getActiveWindow().size[0] // 2,
                                  gw.getActiveWindow().top + int(gw.getActiveWindow().size[1] * 0.3))
                        pyautogui.moveTo(coords[0], coords[1])
                        pyautogui.click()

                        # Envia TAB e ENTER do teclado físico
                        pyautogui.press('tab')
                        time.sleep(0.5)
                        pyautogui.press('enter')
                        time.sleep(5)
                        break

                self.driver.get('chrome-extension://lnidijeaekolpfeckelhkomndglcglhh/sidepanel.html')

            def codes_2_fac(self):
                element_sistema = '//input[(contains(@placeholder,"selecione um sistema pra acessar") or contains(@name, "system") or contains(@placeholder,"Busque ou selecione um sistema")) and contains(@role, "combobox")]'
                element_email = '//input[contains(@placeholder,"Insira aqui o seu email") or contains(@placeholder,"e-mail")]'
                element_certificado = "//input[(contains(@name,'certificate') or contains(@placeholder,'certificado')) and not(contains(@type, 'hidden'))]"
                email = 'eliezer@bcfox.com.br'
                element_code = '//input[contains(@placeholder,"Digite aqui o código que enviamos para o seu e-mail") or contains(@placeholder,"código") or contains(@type,"password")]'
                element_submit = '//button[contains(@type, "submit")]'
                code_insertion = False

                def _logout():
                    tools.find_element_with_wait(By.XPATH, f"//button[contains(text(),'Sair')]").click()
                    time.sleep(4)
                    buttonsair = tools.find_elements_with_wait(By.XPATH, f"//button[contains(text(),'Sair')]")
                    buttonsair[1].click()
                    time.sleep(10)
                    result_login = self.codes_2_fac()
                    self.logout_check = True
                    return result_login

                def _select_system(code_add = False):
                    # Check the code result
                    for _ in range(30):
                        if code_add:
                            break
                        # Correct
                        try:
                            certificate = tools.find_element_with_wait(By.XPATH,f"{element_certificado}", timeout=2)
                            certificate.clear()
                            time.sleep(0.5)
                            certificate.send_keys(self.certificate)
                            certificate.clear()
                            certificate.send_keys(self.certificate)
                            time.sleep(0.5)
                            list_certific = tools.find_elements_with_wait(By.XPATH, '//div[contains(@role,"option")]')
                            if list_certific: # caso encontrar ele vai iniciar o clique no primeiro resultado.
                                list_certific[0].click()
                            else:
                                break
                            time.sleep(1)
                            code_add = True
                            break
                        except:
                            # caso não encontre o respectivo advogado, sera deslogado e iniciado novamente o processo de login
                            pass

                    time.sleep(4)
                    code_add = False
                    for _ in range(30):
                        try:
                            # input('\n\n > Selecione o sistema e aperte alguma tecla.')
                            clique_open = tools.find_element_with_wait(By.XPATH, element_sistema)
                            clique_open.click()
                            list_sistemas = tools.find_element_with_wait(By.XPATH, '//div[contains(@role,"listbox")]')
                            if 'nada encontrado' in list_sistemas.text.lower():
                                return _logout()

                            clique_open.send_keys(self.system)
                            time.sleep(1)
                            list_sistemas = tools.find_elements_with_wait(By.XPATH, '//div[contains(@role,"option")]')
                            if len(list_sistemas) <= 2 and len(list_sistemas)!=0:
                                list_sistemas[0].click()
                                code_add = True
                                break
                            else:
                                mostrar_mensagem(
                                    'Mais de um ou nenhum sistema encontrado, verifique o nome do sistema no WHOOM e coloque um nome único na função')
                                raise ValueError('Mais de um sistema encontrado, verifique o nome')
                        except:
                            pass

                    if code_add:
                        return True

                def _check_system():
                    try:
                        certifcate = tools.find_element_with_wait(By.XPATH, f'{element_certificado}', timeout=2)
                        certifcate.clear()
                        if 'Não foi possível obter os tokens para esse certificado' in self.driver.page_source:
                            return _logout()
                        return _select_system()
                    except:
                        return False

                def _page_email():
                    self.driver.get('chrome-extension://lnidijeaekolpfeckelhkomndglcglhh/sidepanel.html')

                    # Request the code
                    for _ in range(50):  # Wait the extension to load
                        time.sleep(1)
                        try:
                            tools.find_element_with_wait(By.XPATH, f'{element_email}', timeout=1).send_keys(f'{email}')
                            time.sleep(1)
                            break
                        except:
                            self.driver.get('chrome-extension://lnidijeaekolpfeckelhkomndglcglhh/sidepanel.html')

                    # Envia o código pro email, o for é só para tratativa de bugs
                    for _ in range(10):
                        try:
                            tools.find_element_with_wait(By.XPATH, f'{element_code}', timeout=1)
                            break

                        except:
                            try:
                                element = tools.find_element_with_wait(By.XPATH, f'{element_email}', timeout=1)
                                element.clear()
                                element.send_keys(f'{email}')
                                tools.find_element_with_wait(By.XPATH, f'{element_submit}').click()
                                time.sleep(1)
                            except:
                                break

                    # Attempts the new codes until success or requests limit
                    for _ in range(code_timeout):
                        def adicionar_code():
                            responses = api.invoke_get_codes()
                            if not responses:
                                time.sleep(2)
                                return [], None
                            # Try new codes
                            code_insertion = False
                            for response in responses:
                                time.sleep(1)

                                if 'Escolha um certificado' in self.driver.page_source:
                                    break

                                CODE = response['CODIGO']
                                ID = response['ID']

                                element = tools.find_element_with_wait(By.XPATH, '//input[@type="password"]')
                                element.clear()
                                element.send_keys(CODE)
                                button_entrar_click = False
                                for _ in range(20):
                                    try:
                                        button = tools.find_element_with_wait(By.XPATH, f'{element_submit}', timeout=2)
                                        if button.get_attribute('disabled'):
                                            time.sleep(1.5)
                                            continue
                                        button.click()
                                        button_entrar_click = True
                                        if 'Escolha um certificado' in self.driver.page_source:
                                            break

                                        time.sleep(1)
                                    except:
                                        break

                                    if int(_) == 20 and button_entrar_click == False:
                                        raise TimeoutError('Código Whom falhou ao entrar')

                                return responses, ID

                        responses, ID = adicionar_code()

                        if 'Sair' in self.driver.page_source and not 'Verifique seu e-mail' in self.driver.page_source:
                            api.invoke_update_status(ID)  # FIX: Update
                            break

                        if 'Verifique seu e-mail':
                            responses, ID = adicionar_code()

                        for _ in range(60):
                            if not (responses):
                                responses, ID = adicionar_code()
                                time.sleep(1)
                            else:
                                break

                        if not (responses):
                            return False
                            raise TimeoutError('Código WHOOM não chegou dentro do timeout estabelecido')
                            # Wrong

                        try:
                            tools.find_element_with_wait(By.XPATH, "//span[contains(text(), 'Senha inválida')]",
                                                         timeout=1)
                            tools.find_element_with_wait(By.XPATH, "//button[text()='Voltar']", timeout=1).click()
                            code_insertion = False
                            break

                        except:
                            pass

                        if 'Sair' in self.driver.page_source and not 'Verifique seu e-mail' in self.driver.page_source:
                            api.invoke_update_status(ID)  # FIX: Update
                            break

                try:
                    code_insertion = _check_system()
                    if self.logout_check:
                        return code_insertion

                    if not code_insertion:
                        _page_email()
                        code_insertion = _check_system()
                        if self.logout_check:
                            return code_insertion
                except:
                    _page_email()
                    code_insertion = _check_system()
                    if self.logout_check:
                        return code_insertion

                if code_insertion:
                    tools.find_element_with_wait(By.XPATH,
                                                 f"//button[contains(@data-testid,'submit') or contains(text(), 'Acessar')]").click()
                    print('Logado')
                else:
                    return False
                    raise TimeoutError('Código WHOOM não chegou dentro do timeout estabelecido')

                time.sleep(2)
                janelas = self.driver.window_handles
                time.sleep(2)
                attempt = 0
                while ('whoom' in self.driver.title.strip().lower() or 'doc9' in self.driver.title.strip().lower()) and attempt <= 180:
                    if 'Sistema temporariamente indisponível. Tente novamente mais tarde.' in self.driver.page_source:
                        raise SystemError('Sistema indisponivel no momento.')

                    if 'O sistema que você está tentando acessar está em manutenção devido a atualizações do tribunal' in self.driver.page_source:
                        tools.find_element_with_wait(By.XPATH,
                                                     f"//button[contains(text(), 'Prosseguir')]").click()
                    try:
                        tools.find_element_with_wait(By.XPATH, f'{element_email}', timeout=1).send_keys(f'{email}')
                        time.sleep(1)
                        _page_email()
                        code_insertion = _check_system()
                        if self.logout_check:
                            return code_insertion
                    except:
                        pass
                    if len(self.driver.window_handles) > 1:
                        time.sleep(3)
                        protection.stop()
                        self.driver.switch_to.window(self.driver.window_handles[0])
                        self.driver.close()
                        self.driver.switch_to.window(self.driver.window_handles[-1])
                        return True
                    time.sleep(1)
                    attempt += 1
                time.sleep(5)

                if attempt >= 180:
                    return False
                    raise SystemError('Whoom congelou no conectar com site.')

                protection.stop()
                if len(self.driver.window_handles) > 1:
                    self.driver.switch_to.window(self.driver.window_handles[0])
                    self.driver.close()
                    self.driver.switch_to.window(self.driver.window_handles[-1])
                    return True

        # Instances
        tools = tool()
        api = invokes_whoom()
        protection = Pop_up_protection()
        bot = whoom_codes()

        # Operacional
        bot.extension_check()
        protection.start()
        result = bot.codes_2_fac()
        if not result:
            protection.stop()
        return result