"""
===============================================================================
SAP GUI Desktop Automation
===============================================================================

Automação robusta para SAP GUI Desktop utilizando SAP GUI Scripting (COM).

"""

# pyright: reportOptionalMemberAccess=false

from __future__ import annotations

import time
from typing import Any

import win32com.client

from logger_config import logger


class SAPConnectionError(Exception):
    pass


class SAPSessionError(Exception):
    pass


class SAPTransactionError(Exception):
    pass


class SAPElementNotFound(Exception):
    pass


class SAPGuiAutomation:
    """
    Classe principal para automação SAP GUI Desktop.
    """

    def __init__(self, connection_index: int = 0, session_index: int = 0):
        self.connection_index = connection_index
        self.session_index = session_index
        self.application: Any | None = None
        self.connection: Any | None = None
        self.session: Any | None = None

    def _require_session(self) -> None:
        if self.session is None:
            raise SAPSessionError("Sessão não inicializada. Chame connect() primeiro.")

    def _find(self, element_id: str):
        """
        Wrapper seguro para findById.
        """
        self._require_session()
        try:
            return self.session.findById(element_id)
        except Exception as e:
            raise SAPElementNotFound(f"Elemento não encontrado: {element_id}") from e

    def connect(self) -> None:
        """
        Conecta ao SAP GUI já aberto.

        Raises:
            SAPConnectionError
        """
        try:
            sap_gui = win32com.client.GetObject("SAPGUI")
        except Exception as e:
            raise SAPConnectionError(
                "SAP GUI não está aberto ou Scripting não está habilitado."
            ) from e

        try:
            self.application = sap_gui.GetScriptingEngine
            if self.application is None:
                raise SAPConnectionError("Scripting SAP não disponível.")

            self.connection = self.application.Children(self.connection_index)
            self.session = self.connection.Children(self.session_index)

            logger.info("Conectado ao SAP GUI com sucesso.")

        except Exception as e:
            raise SAPConnectionError(f"Erro ao obter sessão SAP: {e}") from e

    def run_transaction(self, transaction_code: str) -> None:
        """
        Executa transação SAP.

        Args:
            transaction_code (str): Código da transação.
        """
        self._require_session()
        try:
            self.session.StartTransaction(transaction_code)
            logger.info(f"Transação executada: {transaction_code}")
        except Exception as e:
            raise SAPTransactionError(str(e)) from e

    def close_transaction(self) -> None:
        """
        Sai da transação atual (/n).
        """
        self._require_session()
        self.session.sendVKey(15)

    def maximize_window(self) -> None:
        """Maximiza janela principal."""
        self._require_session()
        self._find("wnd[0]").maximize()

    def set_field(self, field_id: str, value: str) -> None:
        element = self._find(field_id)
        element.text = value
        logger.info(f"Campo preenchido: {field_id}")

    def get_field(self, field_id: str) -> str:
        element = self._find(field_id)
        return element.text

    def press_button(self, button_id: str) -> None:
        element = self._find(button_id)
        element.press()
        logger.info(f"Botão pressionado: {button_id}")

    def press_enter(self) -> None:
        self._require_session()
        self.session.sendVKey(0)

    def get_status_message(self) -> str:
        """
        Retorna texto da barra de status.
        """
        try:
            return self._find("wnd[0]/sbar").text
        except SAPElementNotFound:
            return ""

    def get_status_type(self) -> str:
        """
        Retorna tipo da mensagem (S, E, W, I).
        """
        try:
            return self._find("wnd[0]/sbar").MessageType
        except SAPElementNotFound:
            return ""

    def is_popup_open(self) -> bool:
        """
        Verifica se wnd[1] existe.
        """
        try:
            self._find("wnd[1]")
            return True
        except SAPElementNotFound:
            return False

    def close_popup(self) -> None:
        """
        Fecha popup ativo (wnd[1]).
        """
        try:
            popup = self._find("wnd[1]")
            popup.Close()
            logger.info("Popup fechado.")
        except SAPElementNotFound:
            logger.info("Nenhum popup aberto.")

    def read_table_cell(self, table_id: str, row: int, column: str) -> str:
        """
        Lê valor de célula em grid ALV.

        Args:
            table_id (str)
            row (int)
            column (str)

        Returns:
            str
        """
        table = self._find(table_id)
        try:
            return table.GetCellValue(row, column)
        except Exception as e:
            raise SAPElementNotFound(f"Erro ao acessar tabela {table_id}") from e

    def take_screenshot(self, path: str) -> None:
        """
        Salva screenshot da janela principal.
        """
        try:
            self._find("wnd[0]").HardCopy(path)
            logger.info(f"Screenshot salva em: {path}")
        except Exception as e:
            logger.error(f"Erro ao capturar screenshot: {e}")

    def wait(self, seconds: float) -> None:
        time.sleep(seconds)
