import os
import sys
import re
import json
import time
import argparse
from pathlib import Path
from datetime import date
from collections import defaultdict

BASE_DIR = Path(r"C:\fuentes\proyectos delphi\RAUL_ASENCIO\GESTION")
SRC_DIR = BASE_DIR / "src"
AGENTS_DIR = BASE_DIR / ".agents"
WORKFLOWS_DIR = AGENTS_DIR / "workflows"
DOMAINS_DIR = AGENTS_DIR / "domains"
INDEX_FILE = AGENTS_DIR / "INDEX.md"
GRAPH_FILE = AGENTS_DIR / "codebase_graph.json"
SQL_STRUCT_FILE = BASE_DIR / "PASTELERIAS-struct.sql"

DOMAINS_DIR.mkdir(parents=True, exist_ok=True)
WORKFLOWS_DIR.mkdir(parents=True, exist_ok=True)

TABLE_REGEX = re.compile(r'\b(?:FROM|JOIN|UPDATE|INTO)\s+`?(ge_[a-z0-9_]+|ob_[a-z0-9_]+|edi_[a-z0-9_]+)`?', re.IGNORECASE)
DFM_OBJECT_REGEX = re.compile(r'object\s+([A-Za-z0-9_]+):\s+([A-Za-z0-9_]+)', re.IGNORECASE)

def parse_ddl_tables():
    if not SQL_STRUCT_FILE.exists():
        return set()
    text = SQL_STRUCT_FILE.read_text(encoding='utf-8', errors='ignore')
    tables = re.findall(r'CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?`?([A-Za-z0-9_]+)`?', text, re.IGNORECASE)
    return set(t.lower() for t in tables)

def run_indexing(auto_create_workflows=False):
    start_time = time.time()
    pas_files = [p for p in SRC_DIR.rglob("*.pas") if "__history" not in str(p) and "__recovery" not in str(p)]
    pas_files.sort(key=lambda p: p.name.lower())

    ddl_tables = parse_ddl_tables()

    file_data = {}
    all_tables = set()
    table_to_modules = defaultdict(set)

    for p in pas_files:
        try:
            text = p.read_text(encoding='utf-8', errors='ignore')
        except Exception:
            text = ''
            
        tokens = set(re.findall(r'\b[A-Za-z0-9_]+\b', text))
        unit_match = re.search(r'unit\s+([A-Za-z0-9_]+);', text, re.IGNORECASE)
        unit_name = unit_match.group(1) if unit_match else p.stem
        
        classes = re.findall(r'\b(T[A-Za-z0-9_]+)\s*=\s*class\b', text)
        form_class = next((c for c in classes if 'frm' in c.lower() or 'form' in c.lower()), classes[0] if classes else '')
        
        uses_list = []
        uses_matches = re.findall(r'uses\s+([^;]+);', text, re.IGNORECASE)
        for um in uses_matches:
            for u in um.split(','):
                u_clean = u.strip()
                if u_clean and u_clean not in uses_list:
                    uses_list.append(u_clean)
                    
        tables_found = set(t.lower() for t in TABLE_REGEX.findall(text))
        
        dfm_path = p.with_suffix('.dfm')
        dfm_components = []
        if dfm_path.exists():
            try:
                dfm_text = dfm_path.read_text(encoding='utf-8', errors='ignore')
                dfm_components = DFM_OBJECT_REGEX.findall(dfm_text)
                for dt in TABLE_REGEX.findall(dfm_text):
                    tables_found.add(dt.lower())
            except Exception:
                pass
                
        for t in tables_found:
            all_tables.add(t)
            table_to_modules[t].add(p.name)
                
        m_intf = re.search(r'interface\s+(.*?)\s+implementation', text, re.DOTALL | re.IGNORECASE)
        intf_text = m_intf.group(1) if m_intf else ''
        methods = re.findall(r'(?:procedure|function|class\s+procedure|class\s+function)\s+([A-Za-z0-9_]+)', intf_text, re.IGNORECASE)
        methods = [m for m in set(methods) if m.lower() not in ('create', 'destroy', 'free', 'init', 'done')]
        
        wf_name = p.stem.lower() + ".md"
        wf_path = WORKFLOWS_DIR / wf_name
        has_wf = wf_path.exists()
        
        # Auto-generate missing workflow template if requested
        if auto_create_workflows and not has_wf:
            wf_content = [
                f"# Skill: {p.stem.lower()}",
                "",
                "## Metadata",
                f"- **Archivo**: {p.name}",
                f"- **Formulario Delphi**: {form_class or unit_name}",
                f"- **Última Revisión**: {date.today().isoformat()}",
                "",
                "## Overview",
                f"Módulo de {p.parent.name} para gestión en AGRIGEST.",
                "",
                "## Tables Involved (Tablas Implicadas)",
                "",
                "| Tabla | Modo | Uso |",
                "| :--- | :---: | :--- |"
            ]
            for t in sorted(list(tables_found)):
                wf_content.append(f"| `{t}` | R+W | Operaciones del módulo |")
            if not tables_found:
                wf_content.append("| — | — | No utiliza tablas directas |")
            wf_content.append("")
            wf_content.append("## Observaciones y Restricciones")
            wf_content.append("Módulo generado e indexado automáticamente.")
            wf_content.append("")
            wf_path.write_text("\n".join(wf_content), encoding='utf-8')
            has_wf = True

        rel_path = p.relative_to(BASE_DIR).as_posix()
        
        file_data[p.name] = {
            'file': p.name,
            'unit': unit_name,
            'rel_path': rel_path,
            'folder': p.parent.name,
            'form_class': form_class,
            'classes': classes,
            'uses': uses_list,
            'tables': sorted(list(tables_found)),
            'dfm_components_count': len(dfm_components),
            'methods': sorted(methods),
            'tokens': tokens,
            'has_wf': has_wf,
            'wf_name': wf_name,
            'called_by': []
        }

    for fname, fdata in file_data.items():
        for other_fname, other_fdata in file_data.items():
            if other_fname == fname:
                continue
            if fdata['unit'] in other_fdata['uses'] or (fdata['form_class'] and fdata['form_class'] in other_fdata['tokens']):
                fdata['called_by'].append(other_fname)
        fdata['called_by'].sort()

    orphan_units = [fname for fname, fdata in file_data.items() if not fdata['called_by'] and fname not in ('frm_Main.pas', 'dmg_Main.pas', 'dmg_Calidad.pas', 'Gestion.dpr')]

    domain_specs = [
        {"id": "01_ventas_facturacion", "title": "Ventas, Facturación y VERI*FACTU", "keywords": ["factura", "pedido", "albaran", "presupuesto", "verifactu", "sif", "edicom", "webimporter", "printservice"], "description": "Ciclo comercial completo de ventas, expediciones de albaranes, facturación y sellado criptográfico VERI*FACTU.", "mermaid": "graph LR\n    Pedido[\"Pedidos (EDI / Web / PDF / Manual)\"] --> Albaran[\"Albaranes\"]\n    Albaran --> Envio[\"Envíos (Nacex / Tipsa)\"]\n    Albaran --> Factura[\"Facturas\"]\n    Factura --> VeriFactu[\"VERI*FACTU (AEAT / SIF)\"]"},
        {"id": "02_compras_proveedores", "title": "Compras y Proveedores", "keywords": ["prov", "factusol"], "description": "Gestión de compras, albaranes, facturas y presupuestos de proveedor, importación Factusol.", "mermaid": "graph LR\n    PresupuestoProv[\"Presupuestos Proveedor\"] --> AlbaranProv[\"Albaranes Proveedor\"]\n    AlbaranProv --> FacturaProv[\"Facturas Proveedor\"]\n    Factusol[\"Factusol (ODBC)\"] --> FacturaProv"},
        {"id": "03_produccion_obradores", "title": "Producción, Obradores y Trazabilidad", "keywords": ["obrador", "receta", "materia", "lote", "inventario", "stock", "fichatecnica"], "description": "Gestión de recetas, órdenes de elaboración, control de materias primas, lotes y stock envasado.", "mermaid": "graph LR\n    MateriasPrimas[\"Materias Primas\"] --> Recetas[\"Recetas / Fórmulas\"]\n    Recetas --> Elaboracion[\"Obradores / Elaboración\"]\n    Elaboracion --> Lotes[\"Lotes de Producción\"]\n    Lotes --> Stock[\"Stock Envasado\"]"},
        {"id": "04_calidad_ifs", "title": "Calidad, Auditoría e IFS", "keywords": ["calidad", "alergeno", "incidencia", "formatospresentacion", "reporttemplate"], "description": "Cumplimiento normativo IFS, puntos de control, agenda de verificación diaria, registro de incidencias y alérgenos.", "mermaid": "graph LR\n    Prerrequisitos[\"Prerrequisitos / Docs\"] --> Planificador[\"Agenda / Planificador\"]\n    Planificador --> Verificacion[\"Verificación Diaria\"]\n    Verificacion --> Incidencias[\"Registro de Incidencias\"]\n    Verificacion --> Trazabilidad[\"Trazabilidad Maestra\"]"},
        {"id": "05_logistica_envios", "title": "Logística, Transporte y Envíos", "keywords": ["envio", "tipsa", "nacex", "transportista", "firma", "formatosenvio"], "description": "Integración con agencias de transporte (Nacex REST, Tipsa SOAP), generación de etiquetas PDF y recogida con firma.", "mermaid": "graph LR\n    Albaran[\"Albarán Venta\"] --> EnvioEditor[\"Configuración Envío\"]\n    EnvioEditor --> Nacex[\"NACEX REST API\"]\n    EnvioEditor --> Tipsa[\"TIPSA SOAP WebService\"]\n    EnvioEditor --> Firma[\"Firma Digital Recogida\"]"},
        {"id": "06_sistema_ia", "title": "Sistema, Seguridad e Inteligencia Artificial", "keywords": ["main", "login", "user", "usuario", "company", "empresa", "dbconfig", "dberror", "chatia", "iaservice", "theme", "autoscale", "printoption", "email", "gs1", "validacion"], "description": "Menú principal, autenticación, conexión a base de datos, servicio IA (Gemini multimodal) y utilidades transversales.", "mermaid": "graph LR\n    Login[\"Login Usuario\"] --> SelectCompany[\"Selección Empresa\"]\n    SelectCompany --> Main[\"Menú Principal (MDI)\"]\n    Main --> ChatIA[\"Chat IA (Gemini SQL)\"]\n    Main --> PDFOrder[\"Extracción Pedidos PDF IA\"]"},
        {"id": "07_maestros_almacenes", "title": "Maestros Generales, Clientes y Artículos", "keywords": ["cliente", "articulo", "familia", "tipoiva", "almacen", "serie", "codigosbarras"], "description": "Mantenimiento de entidades maestras: clientes, destinos, artículos, EANs, tarifas, familias, series y almacenes.", "mermaid": "graph LR\n    Empresa[\"Empresa\"] --> Series[\"Series Facturación\"]\n    Empresa --> Clientes[\"Clientes + Destinos EAN\"]\n    Empresa --> Articulos[\"Artículos + EANs + Tarifas\"]\n    Articulos --> Familias[\"Familias Jerárquicas\"]"}
    ]

    domain_assignments = {d['id']: [] for d in domain_specs}
    assigned_files = set()

    for d in domain_specs:
        for fname, fdata in file_data.items():
            if fname in assigned_files:
                continue
            fname_lower = fname.lower()
            if any(kw in fname_lower for kw in d['keywords']):
                domain_assignments[d['id']].append(fdata)
                assigned_files.add(fname)

    for fname, fdata in file_data.items():
        if fname not in assigned_files:
            domain_assignments["07_maestros_almacenes"].append(fdata)
            assigned_files.add(fname)

    for d in domain_specs:
        dom_id = d['id']
        dom_file = DOMAINS_DIR / f"{dom_id}.md"
        assigned = domain_assignments[dom_id]
        dom_tables = set(t for f in assigned for t in f['tables'])
        
        dom_md = []
        dom_md.append(f"# Dominio {dom_id[:2]}: {d['title']}\n")
        dom_md.append(f"## Overview\n{d['description']}\n")
        dom_md.append("## Diagrama de Flujo de Negocio\n```mermaid\n" + d['mermaid'] + "\n```\n")
        dom_md.append("## Módulos y Unidades Delphi del Dominio\n")
        dom_md.append("| Capa | Archivo | Clase / Formulario | Tablas BD | Workflow |")
        dom_md.append("| :--- | :--- | :--- | :--- | :--- |")
        
        for f in sorted(assigned, key=lambda x: (x['folder'], x['file'])):
            pas_link = f"[{f['file']}](file:///c:/fuentes/proyectos%20delphi/RAUL_ASENCIO/GESTION/{f['rel_path']})"
            cls_name = f"`{f['form_class']}`" if f['form_class'] else f"`{f['unit']}`"
            tbl_str = ", ".join(f"`{t}`" for t in f['tables'][:3]) if f['tables'] else "—"
            if len(f['tables']) > 3:
                tbl_str += f" (+{len(f['tables'])-3})"
            wf_link = f"[{f['wf_name']}](file:///c:/fuentes/proyectos%20delphi/RAUL_ASENCIO/GESTION/.agents/workflows/{f['wf_name']})" if f['has_wf'] else "—"
            dom_md.append(f"| **{f['folder']}** | {pas_link} | {cls_name} | {tbl_str} | {wf_link} |")
            
        dom_md.append("\n## Tablas de Base de Datos Vinculadas\n")
        dom_md.append("| Tabla | Módulos de este Dominio que la Utilizan |")
        dom_md.append("| :--- | :--- |")
        for t in sorted(list(dom_tables)):
            mods = [f['file'] for f in assigned if t in f['tables']]
            dom_md.append(f"| `{t}` | {', '.join(f'`{m}`' for m in mods)} |")
            
        dom_file.write_text("\n".join(dom_md) + "\n", encoding='utf-8')

    today_str = date.today().strftime("%Y-%m-%d")
    index_md = [
        "# AGRIGEST/GESTION — Índice Maestro de Módulos (Índice Rápido)\n",
        "> **Uso**: Punto de entrada de navegación para agentes y desarrolladores.",
        "> Consulta el dominio específico en `.agents/domains/` o la ficha en `.agents/workflows/`.",
        f"> **Última Actualización**: {today_str} | **Módulos**: {len(file_data)} | **Tablas BD**: {len(all_tables)}\n",
        "---\n",
        "## 🗺️ Mapa de Dominios de Negocio\n",
        "| Dominio | Módulos | Tablas BD | Enlace |",
        "| :--- | :---: | :---: | :--- |"
    ]
    for d in domain_specs:
        assigned = domain_assignments[d['id']]
        d_tables = set(t for f in assigned for t in f['tables'])
        link = f"[{d['title']}](file:///c:/fuentes/proyectos%20delphi/RAUL_ASENCIO/GESTION/.agents/domains/{d['id']}.md)"
        index_md.append(f"| **{d['id'][:2]}** | {len(assigned)} | {len(d_tables)} | {link} |")
    index_md.append("\n---\n")

    index_md.append("## 🏛️ Arquitectura de Capas\n```")
    index_md.append(f"┌────────────────────────────────────────────────────────┐")
    index_md.append(f"│  src/UI ({sum(1 for f in file_data.values() if f['folder']=='UI')} forms)    — Formularios VCL (BaseList/Base)│")
    index_md.append(f"├────────────────────────────────────────────────────────┤")
    index_md.append(f"│  src/Logic ({sum(1 for f in file_data.values() if f['folder']=='Logic')} units)  — Servicios (IA, Envíos, SIF)   │")
    index_md.append(f"├────────────────────────────────────────────────────────┤")
    index_md.append(f"│  src/Common ({sum(1 for f in file_data.values() if f['folder']=='Common')} units) — Helpers, Theming, Tipos SIF   │")
    index_md.append(f"├────────────────────────────────────────────────────────┤")
    index_md.append(f"│  src/Data ({sum(1 for f in file_data.values() if f['folder']=='Data')} DMs)      — dmg_Main (BD), dmg_Calidad    │")
    index_md.append(f"└────────────────────────────────────────────────────────┘")
    index_md.append("```\n")

    index_md.append("## 📋 Reglas Clave y Convenciones Obligatorias\n")
    index_md.append("1. **Base de Datos**: FireDAC con parámetros obligatorios. Nombres exactos verificados contra `PASTELERIAS-struct.sql`.")
    index_md.append("2. **UTF-8 y Unicode**: En `TFDQuery`, asignar strings con `DataType := ftWideString` y `AsWideString`.")
    index_md.append("3. **Variables**: Prefijo `L` para locales, `A` para argumentos. **PROHIBIDO** variables en línea `var x := ...`.")
    index_md.append("4. **Eventos `TField.OnGetText`**: Deben ser miembros de clase, nunca procedimientos globales.")
    index_md.append("5. **Checkboxes en DBGrid**: Automáticos para campos de estado activo (`activo`/`active` o booleanos).")
    index_md.append("6. **Grafo de Código**: Consulta de dependencias exactas en [`.agents/codebase_graph.json`](file:///c:/fuentes/proyectos%20delphi/RAUL_ASENCIO/GESTION/.agents/codebase_graph.json).\n")

    INDEX_FILE.write_text("\n".join(index_md) + "\n", encoding='utf-8')

    graph_export = {
        'version': '5.0',
        'generated_at': date.today().isoformat(),
        'stats': {
            'total_units': len(file_data),
            'total_tables_detected': len(all_tables),
            'ddl_tables_count': len(ddl_tables),
            'workflows_count': sum(1 for f in file_data.values() if f['has_wf']),
            'orphan_units_count': len(orphan_units)
        },
        'orphan_units': orphan_units,
        'tables_map': {t: sorted(list(mods)) for t, mods in sorted(table_to_modules.items())},
        'modules': {
            k: {
                'layer': v['folder'],
                'unit': v['unit'],
                'class': v['form_class'],
                'tables': v['tables'],
                'dfm_components': v['dfm_components_count'],
                'uses': [u for u in v['uses'] if u in [f['unit'] for f in file_data.values()]],
                'called_by': v['called_by'],
                'methods_count': len(v['methods']),
                'has_workflow': v['has_wf'],
                'workflow_file': v['wf_name'] if v['has_wf'] else None
            }
            for k, v in file_data.items()
        }
    }
    GRAPH_FILE.write_text(json.dumps(graph_export, indent=2, ensure_ascii=False), encoding='utf-8')
    print(f"[Iteración 5] Reindexación y Validación DDL completada en {time.time()-start_time:.2f}s!")
    return file_data, all_tables, ddl_tables

def cli_query(keyword):
    if not GRAPH_FILE.exists():
        run_indexing()
    data = json.loads(GRAPH_FILE.read_text(encoding='utf-8'))
    kw = keyword.lower()
    
    print(f"\n🔍 RESULTADOS DE BÚSQUEDA PARA '{keyword}':")
    print("=" * 60)
    
    matching_tables = {t: mods for t, mods in data.get('tables_map', {}).items() if kw in t.lower()}
    if matching_tables:
        print(f"\n📊 TABLAS DE BASE DE DATOS ({len(matching_tables)} encontradas):")
        for t, mods in matching_tables.items():
            print(f"  • {t} -> Usada en {len(mods)} módulos: {', '.join(mods[:5])}{' (+'+str(len(mods)-5)+')' if len(mods)>5 else ''}")
            
    matching_modules = {k: v for k, v in data['modules'].items() if kw in k.lower() or kw in (v.get('class') or '').lower()}
    if matching_modules:
        print(f"\n📦 MÓDULOS DELPHI ({len(matching_modules)} encontrados):")
        for k, v in matching_modules.items():
            print(f"  • {k} [{v['layer']}] ({v['class'] or v['unit']})")
            print(f"    - Tablas: {', '.join(v['tables']) if v['tables'] else 'Ninguna'}")
            print(f"    - Invocado por ({len(v['called_by'])}): {', '.join(v['called_by'][:4])}{' (+'+str(len(v['called_by'])-4)+')' if len(v['called_by'])>4 else ''}")
            print(f"    - Workflow: {'✅ ' + v['workflow_file'] if v['has_workflow'] else '❌ Falta workflow'}")

def cli_check_integrity():
    if not GRAPH_FILE.exists():
        run_indexing()
    data = json.loads(GRAPH_FILE.read_text(encoding='utf-8'))
    ddl_tables = parse_ddl_tables()
    code_tables = set(data.get('tables_map', {}).keys())
    
    print("\n🛡️ AUDITORÍA DE INTEGRIDAD DEL PROYECTO (Delphi + MySQL DDL):")
    print("=" * 60)
    print(f"• Total Unidades Delphi: {data['stats']['total_units']}")
    print(f"• Cobertura de Workflows: {data['stats']['workflows_count']} / {data['stats']['total_units']} ({data['stats']['workflows_count']/data['stats']['total_units']*100:.1f}%)")
    print(f"• Tablas en DDL (PASTELERIAS-struct.sql): {len(ddl_tables)}")
    print(f"• Tablas referenciadas en código: {len(code_tables)}")
    
    unmatched_in_ddl = [t for t in code_tables if t not in ddl_tables and not t.startswith('ge_sif') and not t.startswith('ge_facturas_rect')]
    if unmatched_in_ddl:
        print(f"⚠️ Tablas en código que no están en el DDL base: {', '.join(unmatched_in_ddl[:6])}")
        
    print(f"• Módulos Huérfanos / No referenciados: {len(data['orphan_units'])}")
    if data['orphan_units']:
        print("  - " + ", ".join(data['orphan_units'][:8]) + ("..." if len(data['orphan_units'])>8 else ""))

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="AGRIGEST Delphi Codebase Indexer & Query Tool")
    parser.add_argument('--query', '-q', type=str, help="Buscar tabla, módulo o clase")
    parser.add_argument('--check-integrity', '-c', action='store_true', help="Auditoría de integridad")
    parser.add_argument('--auto-workflows', '-w', action='store_true', help="Crear workflows faltantes")
    
    args = parser.parse_args()
    if args.query:
        cli_query(args.query)
    elif args.check_integrity:
        cli_check_integrity()
    elif args.auto_workflows:
        run_indexing(auto_create_workflows=True)
    else:
        run_indexing()
