import os
import re

# Lista de nombres comunes a ignorar para evitar falsos positivos masivos
IGNORED_FUNCTIONS = {
    'create', 'destroy', 'free', 'execute', 'showmodal', 'show', 'close',
    'formshow', 'formdestroy', 'formcreate', 'formclose', 'formclosequery',
    'btnrefreshclick', 'btnnewclick', 'btneditclick', 'btndeleteclick',
    'btnprintclick', 'btnhelpclick', 'btnsearchclick', 'edtsearchkeydown',
    'edtsearchchange', 'dbgridmaindblclick', 'dbgridmaintitleclick', 'applyallfilters',
    'dosearch', 'loaded', 'formshowhandler', 'formclosehandler'
}

def clean_delphi_comments(code):
    """Elimina comentarios de Delphi (//, { ... }, (* ... *)) para evitar parsear declaraciones comentadas."""
    # Eliminar comentarios de una línea
    code = re.sub(r'//.*$', '', code, flags=re.MULTILINE)
    # Eliminar comentarios de bloque { ... }
    code = re.sub(r'\{[^{}]*\}', '', code)
    # Eliminar comentarios de bloque (* ... *)
    code = re.sub(r'\(\*.*?\*\)', '', code, flags=re.DOTALL)
    return code

def extract_interface_block(code):
    """Extrae el contenido entre la palabra 'interface' y 'implementation' (insensible a mayúsculas)."""
    match = re.search(r'\binterface\b(.*?)\bimplementation\b', code, re.DOTALL | re.IGNORECASE)
    return match.group(1) if match else ""

def get_declared_functions(interface_code):
    """Extrae los nombres de funciones, procedimientos, constructores y destructores de la interfaz."""
    funcs = set()
    # Buscar declaraciones: procedure, function, constructor, destructor, con o sin prefijo class
    pattern = re.compile(
        r'\b(?:class\s+)?(?:procedure|function|constructor|destructor)\s+([a-zA-Z0-9_]+)',
        re.IGNORECASE
    )
    for line in interface_code.splitlines():
        # Limpiar espacios
        line = line.strip()
        if not line:
            continue
        match = pattern.match(line)
        if match:
            func_name = match.group(1)
            if func_name.lower() not in IGNORED_FUNCTIONS:
                funcs.add(func_name)
    return funcs

def scan_uses(pas_file_path):
    """Busca en qué módulos se utiliza cada función/método."""
    # Retorna el contenido de implementation para buscar usos
    try:
        with open(pas_file_path, 'r', encoding='utf-8', errors='ignore') as f:
            code = f.read()
    except Exception:
        return ""
    
    code_clean = clean_delphi_comments(code)
    # Buscaremos a partir de 'implementation' para ver usos reales y evitar la declaración
    impl_match = re.search(r'\bimplementation\b(.*)', code_clean, re.DOTALL | re.IGNORECASE)
    return impl_match.group(1) if impl_match else code_clean

DISABLED = True  # Cambiar a False cuando el usuario desee reactivar la actualización del mapa INDEX.md

def main():
    if DISABLED or os.environ.get('DISABLE_INDEX_UPDATE') == '1':
        print("⚡ Actualización de mapa INDEX.md desactivada temporalmente por solicitud del usuario.")
        return

    project_dir = os.path.dirname(os.path.abspath(__file__))
    index_path = os.path.join(project_dir, '.agents', 'INDEX.md')
    src_dir = os.path.join(project_dir, 'src')
    
    if not os.path.exists(src_dir):
        print(f"Directorio src no encontrado en: {src_dir}")
        return

    # Verificar si INDEX.md existe y es más reciente que todos los archivos .pas
    if os.path.exists(index_path):
        index_mtime = os.path.getmtime(index_path)
        newer_pas_found = False
        for root, _, files in os.walk(src_dir):
            for file in files:
                if file.lower().endswith('.pas'):
                    fp = os.path.join(root, file)
                    if os.path.getmtime(fp) > index_mtime:
                        newer_pas_found = True
                        break
            if newer_pas_found:
                break
        if not newer_pas_found:
            print("⚡ INDEX.md ya está al día. No hay cambios recientes en archivos .pas.")
            return

    print("Escaneando archivos Pascal (.pas) en src/...")
    pas_files = []
    for root, _, files in os.walk(src_dir):
        for file in files:
            if file.lower().endswith('.pas'):
                pas_files.append(os.path.join(root, file))

    # 1. Analizar declaraciones por cada módulo
    module_declarations = {}  # { 'frm_Main.pas': { 'func1', 'func2' } }
    file_contents_impl = {}  # { 'frm_Main.pas': 'codigo implementation' }

    for file_path in pas_files:
        filename = os.path.basename(file_path)
        try:
            with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                code = f.read()
        except Exception as e:
            print(f"No se pudo leer {filename}: {e}")
            continue

        clean_code = clean_delphi_comments(code)
        interface_block = extract_interface_block(clean_code)
        
        funcs = get_declared_functions(interface_block)
        if funcs:
            module_declarations[filename] = funcs
        
        # Guardar la parte de implementacion para buscar usos de las funciones de otros modulos
        impl_code = scan_uses(file_path)
        file_contents_impl[filename] = impl_code

    # 2. Rastrear en qué módulos se utiliza cada función
    # { 'NombreFuncion': { 'decl_in': 'modulo_declarado', 'used_in': ['modulo_uso1', 'modulo_uso2'] } }
    function_map = {}
    
    for decl_mod, funcs in module_declarations.items():
        for func in funcs:
            function_map[func] = {
                'decl_in': decl_mod,
                'used_in': set()
            }

    # Escanear usos
    for filename, impl_code in file_contents_impl.items():
        for func_name, info in function_map.items():
            # Si el módulo actual no es donde se declara y la función se usa
            # Usar regex con word boundary para evitar coincidencias de subcadena
            if filename != info['decl_in']:
                pattern = re.compile(rf'\b{re.escape(func_name)}\b', re.IGNORECASE)
                if pattern.search(impl_code):
                    info['used_in'].add(filename)

    # 3. Actualizar INDEX.md
    index_path = os.path.join(project_dir, '.agents', 'INDEX.md')
    if not os.path.exists(index_path):
        print(f"No se encontró INDEX.md en: {index_path}")
        return

    print("Leyendo INDEX.md...")
    try:
        with open(index_path, 'r', encoding='utf-8') as f:
            index_content = f.read()
    except Exception as e:
        print(f"Error leyendo INDEX.md: {e}")
        return

    # Generar tabla markdown para el mapa de funciones
    map_lines = []
    map_lines.append("## Mapa de Funciones y Procedimientos\n")
    map_lines.append("| Función / Procedimiento / Método | Módulo Declarado | Módulos donde se utiliza |")
    map_lines.append("|:---|:---|:---|")

    # Ordenar alfabéticamente por nombre de función
    sorted_funcs = sorted(function_map.keys(), key=lambda s: s.lower())
    
    added_count = 0
    for func in sorted_funcs:
        info = function_map[func]
        used_in_list = sorted(list(info['used_in']))
        
        # Formatear modulos de uso como links o nombres limpios
        if used_in_list:
            used_str = ", ".join([f"`{m}`" for m in used_in_list])
        else:
            used_str = "*No se detectaron usos externos*"
            
        map_lines.append(f"| `{func}` | `{info['decl_in']}` | {used_str} |")
        added_count += 1

    map_markdown = "\n".join(map_lines) + "\n"

    # Reemplazar o agregar al INDEX.md
    # Buscar si ya existe la sección ## Mapa de Funciones y Procedimientos
    header_pattern = r'## Mapa de Funciones y Procedimientos.*'
    if re.search(header_pattern, index_content, re.DOTALL):
        # Reemplazar desde el encabezado hasta el final del archivo
        new_index_content = re.sub(header_pattern, map_markdown, index_content, flags=re.DOTALL)
        print("Reemplazando sección existente de Mapa de Funciones...")
    else:
        # Añadir al final del archivo
        new_index_content = index_content.rstrip() + "\n\n" + map_markdown
        print("Añadiendo nueva sección de Mapa de Funciones al final...")

    try:
        with open(index_path, 'w', encoding='utf-8') as f:
            f.write(new_index_content)
        print(f"✅ INDEX.md actualizado con éxito. Se mapearon {added_count} funciones/procedimientos personalizados.")
    except Exception as e:
        print(f"Error escribiendo en INDEX.md: {e}")

if __name__ == '__main__':
    main()
