"""
@RAG_CONTEXT
{
  "archivo": "src/rag_indexer/file_parser.py",
  "lineas": 55,
  "tokens_estimados": 482,
  "hash_contenido": "3d2cb17de11f5b4eb66d3d88b13de1a16a32d7f39c0ec96b8dbd8515e1256803",
  "creacion": "2026-03-04",
  "modificacion": "2026-03-19",
  "tags": [
    "json",
    "re",
    "hashlib",
    "COMMENT_SYNTAX",
    ".config"
  ],
  "descripcion_corta": "Parser: hash, extracción e inyección @RAG_CONTEXT + soporte JSON flujos",
  "descripcion_larga": "Módulo de manipulación de archivos fuente. extract_dependencies() detecta imports/require. split_metadata_and_code() separa el bloque @RAG_CONTEXT del código limpio. inject_metadata() reescribe el archivo con metadatos actualizados. Soporte adicional para JSON flujos via _rag_context: split_metadata_and_code_json() extrae el campo _rag_context y hashea el JSON sin él; inject_metadata_json() actualiza _rag_context in-place preservando el orden del resto de campos. calculate_hash() computa SHA256."
}
"""
import re
import json
import hashlib
from pathlib import Path
from .config import COMMENT_SYNTAX, MIRROR_BASE


def extract_dependencies(content, ext):
    deps = set()
    if ext in ['.js', '.ts', '.jsx', '.tsx']:
        deps.update(re.findall(r'import\s+.*?\s+from\s+[\'"]([^\'"]+)[\'"]', content))
        deps.update(re.findall(r'require\([\'"]([^\'"]+)[\'"]\)', content))
    elif ext == '.py':
        deps.update(re.findall(r'import\s+([a-zA-Z0-9_]+)', content))
        deps.update(re.findall(r'from\s+([a-zA-Z0-9_\.]+)\s+import', content))
    return list(deps)


def split_metadata_and_code(content, ext):
    syntax = COMMENT_SYNTAX.get(ext)
    if not syntax:
        return None, content

    start_tag = syntax['start']
    end_tag = syntax['end']

    if content.startswith(start_tag):
        end_idx = content.find(end_tag)
        if end_idx != -1:
            try:
                raw_json = content[len(start_tag):end_idx].strip()
                metadata = json.loads(raw_json)
                clean_code = content[end_idx + len(end_tag):].lstrip()
                return metadata, clean_code
            except json.JSONDecodeError:
                pass

    return None, content


def read_mirror_metadata(root_dir: str, rel_path: str) -> dict | None:
    """Lee el archivo .rag.json del espejo correspondiente a rel_path."""
    mirror_path = Path(root_dir) / MIRROR_BASE / (rel_path + '.rag.json')
    if mirror_path.exists():
        try:
            return json.loads(mirror_path.read_text(encoding='utf-8'))
        except Exception:
            return None
    return None


def write_mirror_metadata(root_dir: str, rel_path: str, metadata: dict) -> None:
    """Escribe el metadata en .ai/rag-mirror/{rel_path}.rag.json. Crea dirs si no existen."""
    mirror_path = Path(root_dir) / MIRROR_BASE / (rel_path + '.rag.json')
    mirror_path.parent.mkdir(parents=True, exist_ok=True)
    mirror_path.write_text(
        json.dumps(metadata, indent=2, ensure_ascii=False) + '\n',
        encoding='utf-8'
    )


def inject_metadata(filepath, original_content, clean_code, metadata, ext):
    """Escribe el metadata en el espejo. El archivo fuente NO se modifica."""
    # Determinar root_dir y rel_path desde filepath
    # filepath es absoluto; buscamos el root_dir como el ancestro que contiene MIRROR_BASE
    # En la práctica, root_dir = os.getcwd() al momento de indexar
    import os
    root_dir = os.getcwd()
    rel_path = os.path.relpath(filepath, root_dir).replace('\\', '/')
    write_mirror_metadata(root_dir, rel_path, metadata)


def calculate_hash(code_string):
    return hashlib.sha256(code_string.encode('utf-8')).hexdigest()


def split_metadata_and_code_json(content: str):
    """
    Para archivos JSON con campo _rag_context (flujos documentados).
    Extrae _rag_context como metadata.
    El 'código' es el JSON serializado sin ese campo, usado solo para hashing.
    Retorna (metadata_dict | None, code_str).
    """
    try:
        data = json.loads(content)
        if '_rag_context' not in data:
            return None, content
        metadata = dict(data['_rag_context'])
        code_data = {k: v for k, v in data.items() if k != '_rag_context'}
        code_str = json.dumps(code_data, ensure_ascii=False, sort_keys=True)
        return metadata, code_str
    except Exception:
        return None, content


def inject_metadata_json(filepath: str, original_content: str, metadata: dict) -> None:
    """
    Escribe el metadata del JSON flujo en el espejo.
    Si el archivo fuente tenía _rag_context inline, lo elimina (migración automática).
    """
    import os
    root_dir = os.getcwd()
    rel_path = os.path.relpath(filepath, root_dir).replace('\\', '/')
    write_mirror_metadata(root_dir, rel_path, metadata)

    # Si el fuente todavía tiene _rag_context inline, eliminarlo
    try:
        data = json.loads(original_content)
        if '_rag_context' in data:
            clean_data = {k: v for k, v in data.items() if k != '_rag_context'}
            new_content = json.dumps(clean_data, indent=2, ensure_ascii=False) + '\n'
            with open(filepath, 'w', encoding='utf-8') as f:
                f.write(new_content)
    except Exception:
        pass
