#!/usr/bin/env python3
"""
Everything3 Official Corrected Wrapper - API Correcto
Wrapper que usa correctamente el API de Everything3 SDK según el código fuente
"""

import ctypes
import os
import logging
from typing import List, Optional

logger = logging.getLogger(__name__)


class Everything3OfficialSearcher:
    """
    Wrapper para Everything SDK 3 usando el API correcto según código fuente
    """

    def __init__(self, dll_path: Optional[str] = None):
        self.dll_path = dll_path
        self.dll = None
        self.client = None
        self._is_initialized = False

    def _load_dll(self) -> bool:
        """Cargar la DLL de Everything SDK 3"""
        try:
            if not self.dll_path or not os.path.exists(self.dll_path):
                logger.error(f"Everything3.dll no encontrada: {self.dll_path}")
                return False

            self.dll = ctypes.WinDLL(self.dll_path)

            # Configurar funciones disponibles
            self._setup_functions()

            logger.info(f"Everything3.dll cargada: {self.dll_path}")
            return True

        except Exception as e:
            logger.error(f"Error cargando Everything3.dll: {e}")
            return False

    def _setup_functions(self):
        """Configurar las funciones según el API oficial"""

        # Everything3_ConnectW - Conectar a instancia
        self.dll.Everything3_ConnectW.argtypes = [ctypes.c_wchar_p]
        self.dll.Everything3_ConnectW.restype = ctypes.c_void_p

        # Everything3_IsDBLoaded - Verificar si la DB está cargada
        self.dll.Everything3_IsDBLoaded.argtypes = [ctypes.c_void_p]
        self.dll.Everything3_IsDBLoaded.restype = ctypes.c_bool

        # Everything3_CreateSearchState - Crear estado de búsqueda
        self.dll.Everything3_CreateSearchState.argtypes = []
        self.dll.Everything3_CreateSearchState.restype = ctypes.c_void_p

        # Everything3_DestroySearchState - Destruir estado de búsqueda
        self.dll.Everything3_DestroySearchState.argtypes = [ctypes.c_void_p]
        self.dll.Everything3_DestroySearchState.restype = ctypes.c_bool

        # Everything3_SetSearchTextW - Establecer texto de búsqueda
        self.dll.Everything3_SetSearchTextW.argtypes = [
            ctypes.c_void_p,
            ctypes.c_wchar_p,
        ]
        self.dll.Everything3_SetSearchTextW.restype = ctypes.c_bool

        # Everything3_Search - Ejecutar búsqueda
        self.dll.Everything3_Search.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
        self.dll.Everything3_Search.restype = ctypes.c_void_p

        # Everything3_GetResultListCount - Obtener número de resultados
        self.dll.Everything3_GetResultListCount.argtypes = [ctypes.c_void_p]
        self.dll.Everything3_GetResultListCount.restype = ctypes.c_size_t

        # Everything3_GetResultFullPathNameW - Obtener ruta completa
        self.dll.Everything3_GetResultFullPathNameW.argtypes = [
            ctypes.c_void_p,  # result_list
            ctypes.c_size_t,  # result_index
            ctypes.c_wchar_p,  # buf
            ctypes.c_size_t,  # buf_size_in_wchars
        ]
        self.dll.Everything3_GetResultFullPathNameW.restype = ctypes.c_size_t

        # Everything3_DestroyResultList - Destruir lista de resultados
        self.dll.Everything3_DestroyResultList.argtypes = [ctypes.c_void_p]
        self.dll.Everything3_DestroyResultList.restype = ctypes.c_bool

        # Everything3_GetLastError - Obtener último error
        self.dll.Everything3_GetLastError.argtypes = []
        self.dll.Everything3_GetLastError.restype = ctypes.c_uint32

    def initialize(self) -> bool:
        """Inicializar Everything API"""
        if self._is_initialized:
            return True

        try:
            # Cargar DLL
            if not self._load_dll():
                return False

            # Conectar a Everything
            if not self._connect_to_everything():
                return False

            self._is_initialized = True
            logger.info("✅ Everything3 inicializado correctamente")
            return True

        except Exception as e:
            logger.error(f"❌ Error inicializando Everything3: {e}")
            return False

    def _connect_to_everything(self) -> bool:
        """Conectar a la instancia de Everything"""
        try:
            # Conectar a Everything 1.5a (versión específica)
            self.client = self.dll.Everything3_ConnectW("1.5a")

            if self.client and self.client != 0:
                # Verificar que la DB esté cargada
                if self.dll.Everything3_IsDBLoaded(self.client):
                    logger.info("✅ Conectado a Everything con DB cargada")
                    return True
                else:
                    logger.warning("⚠️  Conectado pero DB no cargada")
                    return True  # Continuar, la DB puede cargarse después
            else:
                error_code = self.dll.Everything3_GetLastError()
                logger.error(f"❌ Falló conexión, error: {error_code}")
                return False

        except Exception as e:
            logger.error(f"❌ Error conectando: {e}")
            return False

    def is_everything_available(self) -> bool:
        """Verificar si Everything está disponible"""
        return self._is_initialized and self.client is not None

    def search_s7p_files(self, search_directories: List[str]) -> List[str]:
        """Buscar archivos .s7p en los directorios especificados"""
        if not self.is_everything_available():
            logger.error("Everything3 no está disponible")
            return []

        try:
            all_s7p_files = []

            for directory in search_directories:
                s7p_files = self._search_s7p_in_directory(directory)
                all_s7p_files.extend(s7p_files)

            # Eliminar duplicados
            unique_files = list(set(all_s7p_files))
            logger.info(f"Encontrados {len(unique_files)} archivos .s7p únicos")
            return unique_files

        except Exception as e:
            logger.error(f"Error buscando archivos S7P: {e}")
            return []

    def _search_s7p_in_directory(self, directory: str) -> List[str]:
        """Buscar archivos .s7p en un directorio específico usando API correcto"""
        try:
            # Normalizar la ruta del directorio
            directory = os.path.normpath(directory)

            # Crear estado de búsqueda
            search_state = self.dll.Everything3_CreateSearchState()
            if not search_state:
                logger.error("No se pudo crear search state")
                return []

            try:
                # Construir consulta específica para Everything3
                # Usar path: para búsqueda específica en directorio
                query = f'*.s7p path:"{directory}"'
                logger.debug(f"Buscando con consulta: {query}")

                # Establecer texto de búsqueda
                search_success = self.dll.Everything3_SetSearchTextW(
                    search_state, query
                )
                if not search_success:
                    logger.error("No se pudo establecer texto de búsqueda")
                    return []

                # Ejecutar búsqueda
                result_list = self.dll.Everything3_Search(self.client, search_state)
                if not result_list:
                    error_code = self.dll.Everything3_GetLastError()
                    logger.warning(f"Búsqueda falló, error: {error_code}")
                    return []

                try:
                    # Obtener número de resultados
                    num_results = self.dll.Everything3_GetResultListCount(result_list)
                    logger.debug(f"Encontrados {num_results} resultados en {directory}")

                    if num_results == 0:
                        return []

                    # Obtener rutas de los resultados
                    results = []
                    for i in range(num_results):
                        path = self._get_result_path(result_list, i)
                        if path:
                            results.append(path)

                    return results

                finally:
                    # Limpiar lista de resultados
                    self.dll.Everything3_DestroyResultList(result_list)

            finally:
                # Limpiar estado de búsqueda
                self.dll.Everything3_DestroySearchState(search_state)

        except Exception as e:
            logger.error(f"Error buscando en {directory}: {e}")
            return []

    def _get_result_path(self, result_list, index: int) -> Optional[str]:
        """Obtener la ruta completa de un resultado por índice"""
        try:
            # Buffer para la ruta (tamaño máximo de Windows PATH)
            buf_size = 32768
            buf = ctypes.create_unicode_buffer(buf_size)

            # Obtener la ruta completa
            chars_copied = self.dll.Everything3_GetResultFullPathNameW(
                result_list, index, buf, buf_size
            )

            if chars_copied > 0:
                return buf.value
            else:
                logger.warning(f"No se pudo obtener ruta para índice {index}")
                return None

        except Exception as e:
            logger.error(f"Error obteniendo ruta para índice {index}: {e}")
            return None

    def get_everything_diagnosis(self) -> dict:
        """Obtener diagnóstico del estado de Everything"""
        diagnosis = {
            "dll_path": self.dll_path,
            "dll_exists": (os.path.exists(self.dll_path) if self.dll_path else False),
            "dll_loaded": self.dll is not None,
            "client_connected": self.client is not None,
            "is_initialized": self._is_initialized,
            "everything_available": self.is_everything_available(),
        }

        # Información adicional si está conectado
        if self.client:
            try:
                diagnosis["db_loaded"] = self.dll.Everything3_IsDBLoaded(self.client)
            except Exception as e:
                diagnosis["db_loaded_error"] = str(e)

        return diagnosis


def create_everything3_official_searcher(
    dll_path: str,
) -> Optional[Everything3OfficialSearcher]:
    """Crear una instancia del searcher Everything3 oficial corregido"""
    try:
        searcher = Everything3OfficialSearcher(dll_path)
        if searcher.initialize():
            return searcher
        else:
            logger.warning("No se pudo inicializar Everything3 official searcher")
            return None
    except Exception as e:
        logger.error(f"Error creando Everything3 official searcher: {e}")
        return None
