"""
===============================================================================
Decorators
===============================================================================

Biblioteca de decoradores utilitários para automação de processos (RPA),
focada em resiliência, controle de execução, observabilidade e diagnóstico.

Principais recursos:

• rpa_retry
    Retry automático para lidar com instabilidade de sistemas
    (web, desktop, APIs).

• rpa_screenshot_on_error
    Captura automática de screenshot em caso de erro
    (Selenium ou automação de tela via pyautogui).

• rpa_timeout
    Limite máximo de tempo de execução para evitar travamentos.

• rpa_lock_execution
    Impede múltiplas execuções simultâneas utilizando FileLock.

• rpa_log_execution
    Registra início, fim, erro e duração da execução.

Dependências:

    pip install pyautogui
===============================================================================
"""

from __future__ import annotations

import functools
import os
import threading
import time
from collections.abc import Callable
from datetime import datetime
from pathlib import Path
from typing import Any, TypeVar

import pyautogui

from file_lock_manager import FileLock, LockConfig
from logger_config import logger

T = TypeVar("T")


def rpa_retry(
    max_attempts: int = 3,
    delay: float = 0,
    exceptions: tuple[type[Exception], ...] = (Exception,),
) -> Callable[[Callable[..., T]], Callable[..., T]]:
    """
    Decorador para retry automático em fluxos RPA.

    Utilidade em RPA:
        - Sistemas instáveis (web ou desktop)
        - Timeout intermitente
        - Elementos que carregam lentamente
        - Falhas temporárias de rede

    Características:
        - Número máximo de tentativas configurável
        - Delay opcional entre tentativas
        - Exceções específicas podem ser definidas
        - Log detalhado de cada tentativa
        - Propaga erro final caso todas falhem
    """

    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> T:
            last_error: Exception | None = None

            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_error = e
                    logger.warning(
                        f"[RETRY] Tentativa {attempt}/{max_attempts} falhou: {e}"
                    )
                    if attempt < max_attempts and delay > 0:
                        time.sleep(delay)

            raise last_error  # type: ignore

        return wrapper

    return decorator


def rpa_screenshot_on_error(
    driver_attr: str | None = None,
    directory: str = "screenshots",
) -> Callable[[Callable[..., T]], Callable[..., T]]:
    """
    Decorador que captura screenshot automaticamente em caso de erro.

    Utilidade em RPA:
        - Evidência visual de falha
        - Auditoria
        - Diagnóstico rápido
        - Logs corporativos

    Funciona para:
        - Selenium (se driver_attr for informado)
        - Desktop (via pyautogui)

    Args:
        driver_attr:
            Nome do atributo que contém o driver Selenium (ex: "driver").
        directory:
            Diretório onde screenshots serão salvos.
    """

    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> T:
            try:
                return func(*args, **kwargs)
            except Exception as e:
                Path(directory).mkdir(parents=True, exist_ok=True)
                timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
                filename = f"{func.__name__}_{timestamp}.png"
                filepath = os.path.join(directory, filename)

                try:
                    if driver_attr and args:
                        instance = args[0]
                        driver = getattr(instance, driver_attr, None)
                        if driver:
                            driver.save_screenshot(filepath)
                        elif pyautogui:
                            pyautogui.screenshot(filepath)
                    elif pyautogui:
                        pyautogui.screenshot(filepath)

                    logger.error(
                        f"[SCREENSHOT] Capturado erro em {func.__name__}: {filepath}"
                    )
                except Exception as screenshot_error:
                    logger.error(
                        f"[SCREENSHOT ERROR] Falha ao capturar screenshot: {screenshot_error}"
                    )

                raise e

        return wrapper

    return decorator


def rpa_timeout(seconds: int) -> Callable[[Callable[..., T]], Callable[..., T]]:
    """
    Decorador que limita o tempo máximo de execução de uma função.

    Utilidade em RPA:
        - Evitar travamentos indefinidos
        - Garantir SLA de execução
        - Evitar loops infinitos
        - Controlar execução de sistemas lentos

    Implementação:
        - Timeout baseado em thread (cross-platform)
        - Lança TimeoutError caso exceda limite
    """

    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> T:
            result: list[Any] = []
            error: list[Exception] = []

            def target():
                try:
                    result.append(func(*args, **kwargs))
                except Exception as e:
                    error.append(e)

            thread = threading.Thread(target=target)
            thread.start()
            thread.join(seconds)

            if thread.is_alive():
                logger.error(f"[TIMEOUT] {func.__name__} excedeu {seconds}s")
                raise TimeoutError(
                    f"Execução excedeu tempo limite de {seconds} segundos"
                )

            if error:
                raise error[0]

            return result[0]

        return wrapper

    return decorator


def rpa_lock_execution(
    name: str,
    directory: str | None = None,
    debug: bool = False,
) -> Callable[[Callable[..., T]], Callable[..., T]]:
    """
    Decorador que impede múltiplas execuções simultâneas.

    Utilidade em RPA:
        - Jobs agendados (cron)
        - Execuções concorrentes indesejadas
        - Processos críticos

    Integra com FileLock (se disponível).

    Args:
        name:
            Nome do lock.
        directory:
            Diretório onde o lock será criado.
        debug:
            Se True, não cria lock real.
    """

    def decorator(func: Callable[..., T]) -> Callable[..., T]:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> T:
            if not FileLock:
                logger.warning("[LOCK] FileLock não disponível.")
                return func(*args, **kwargs)

            config = LockConfig(directory=directory, debug=debug)
            with FileLock(name, config):
                return func(*args, **kwargs)

        return wrapper

    return decorator


def rpa_log_execution(func: Callable[..., T]) -> Callable[..., T]:
    """
    Decorador que registra início, fim e duração da execução.

    Utilidade em RPA:
        - Auditoria
        - Monitoramento
        - Métricas de performance
        - Logs estruturados

    Recursos:
        - Log de início
        - Log de sucesso
        - Log de erro
        - Log de duração total
    """

    @functools.wraps(func)
    def wrapper(*args: Any, **kwargs: Any) -> T:
        start = time.time()
        logger.info(f"[START] {func.__name__}")

        try:
            result = func(*args, **kwargs)
            duration = round(time.time() - start, 2)
            logger.info(f"[SUCCESS] {func.__name__} finalizado em {duration}s")
            return result
        except Exception as e:
            duration = round(time.time() - start, 2)
            logger.error(f"[ERROR] {func.__name__} falhou após {duration}s: {e}")
            raise e

    return wrapper
