"""
claude_hooks_manager.py — Instalador de hooks Stop de Claude Code
Instala el sistema completo: validador de memoria + menú de sesión + trazabilidad incremental.
Comando: rag-indexer claude-init
"""
import json
import os
import stat
from pathlib import Path
from .logger import get_logger

logger = get_logger("rag_indexer_claude_hooks")

# ── Templates de hooks ────────────────────────────────────────────────────────

ACTUALIZAR_CONTEXTO_HOOK = r"""#!/bin/bash
# Hook Stop — actualiza contexto_activo.json de forma incremental tras cada turno.
# Non-blocking: siempre devuelve allow.

INPUT=$(cat)

# Guardia anti-loop
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0
fi

# Correr context_manager en modo incremental (silencioso)
python3 "$CLAUDE_PROJECT_DIR/.ai/scripts/context_manager.py" --incremental \
  >> "$CLAUDE_PROJECT_DIR/.ai/contexto/context_manager.log" 2>&1 &

exit 0
"""

VALIDADOR_MEMORIA_HOOK = r"""#!/bin/bash
# Hook Stop — Validador de Memoria con escalamiento de confianza.
# Umbral arranca en 2, sube +1 al pasar quiz (hasta techo), vuelve a 2 al fallar.

INPUT=$(cat)
ESTADO_PATH="$CLAUDE_PROJECT_DIR/.ai/contexto/validador_estado.json"

# Guardia anti-loop
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0
fi

# Sin estado o sin sesión activa → esperar inicialización
if [ ! -f "$ESTADO_PATH" ]; then exit 0; fi
SESION=$(jq -r '.sesion_activa' "$ESTADO_PATH")
if [ "$SESION" = "null" ] || [ -z "$SESION" ]; then exit 0; fi

# ── Leer estado actual ──────────────────────────────────────────────────────────
CODIGO=$(jq -r '.sesion_activa.codigo' "$ESTADO_PATH")
UMBRAL_ACTUAL=$(jq -r '.sesion_activa.umbral_actual' "$ESTADO_PATH")
UMBRAL_TECHO=$(jq -r '.sesion_activa.umbral_techo' "$ESTADO_PATH")
COMMITS_DESDE_QUIZ=$(jq -r '.sesion_activa.commits_desde_ultimo_quiz' "$ESTADO_PATH")
COMMITS_TOTAL=$(jq -r '.sesion_activa.commits_total_sesion' "$ESTADO_PATH")
ULTIMO_HASH=$(jq -r '.sesion_activa.ultimo_commit_hash' "$ESTADO_PATH")
QUIZ_EN_PROGRESO=$(jq -r '.sesion_activa.quiz_en_progreso' "$ESTADO_PATH")
MAX_ENTRE=$(jq -r '.sesion_activa.max_commits_entre_quizzes' "$ESTADO_PATH")

# ── Detectar nuevo commit ───────────────────────────────────────────────────────
NUEVO_HASH=$(git -C "$CLAUDE_PROJECT_DIR" log -1 --pretty=format:"%h" 2>/dev/null)

if [ -n "$NUEVO_HASH" ] && [ "$NUEVO_HASH" != "$ULTIMO_HASH" ]; then
  COMMITS_DESDE_QUIZ=$((COMMITS_DESDE_QUIZ + 1))
  COMMITS_TOTAL=$((COMMITS_TOTAL + 1))

  if [ "$COMMITS_DESDE_QUIZ" -gt "$MAX_ENTRE" ]; then
    MAX_ENTRE=$COMMITS_DESDE_QUIZ
  fi

  TMP=$(mktemp)
  jq --arg h "$NUEVO_HASH" \
     --argjson cd "$COMMITS_DESDE_QUIZ" \
     --argjson ct "$COMMITS_TOTAL" \
     --argjson mx "$MAX_ENTRE" \
     '.sesion_activa.ultimo_commit_hash = $h |
      .sesion_activa.commits_desde_ultimo_quiz = $cd |
      .sesion_activa.commits_total_sesion = $ct |
      .sesion_activa.max_commits_entre_quizzes = $mx' \
     "$ESTADO_PATH" > "$TMP" && mv "$TMP" "$ESTADO_PATH"
fi

# ── Verificar si es turno de quiz ───────────────────────────────────────────────
if [ "$COMMITS_DESDE_QUIZ" -lt "$UMBRAL_ACTUAL" ]; then
  exit 0
fi

# ── Normalizar código para comparación ─────────────────────────────────────────
LAST_MSG=$(echo "$INPUT" | jq -r '.last_assistant_message // ""')
CODIGO_NORM=$(echo "$CODIGO" | tr '[:lower:]' '[:upper:]' | tr -d '- ')
MSG_NORM=$(echo "$LAST_MSG" | tr '[:lower:]' '[:upper:]' | tr -d '- \n\r\t')

# ── Procesar respuesta al quiz en curso ────────────────────────────────────────
if [ "$QUIZ_EN_PROGRESO" = "true" ]; then
  if echo "$MSG_NORM" | grep -qF "$CODIGO_NORM"; then
    # ✅ Correcto — subir umbral_actual en +1 hasta el techo
    NUEVO_UMBRAL=$((UMBRAL_ACTUAL + 1))
    if [ "$NUEVO_UMBRAL" -gt "$UMBRAL_TECHO" ]; then
      NUEVO_UMBRAL=$UMBRAL_TECHO
    fi
    TMP=$(mktemp)
    jq --argjson cd 0 \
       --argjson nu "$NUEVO_UMBRAL" \
       '.sesion_activa.commits_desde_ultimo_quiz = $cd |
        .sesion_activa.umbral_actual = $nu |
        .sesion_activa.quiz_en_progreso = false |
        (.sesion_activa.quizzes) += [{"en_commit": (.sesion_activa.commits_total_sesion), "resultado": "correcto", "nuevo_umbral": $nu}]' \
       "$ESTADO_PATH" > "$TMP" && mv "$TMP" "$ESTADO_PATH"
    exit 0
  else
    # ❌ Incorrecto — resetear umbral_actual a 2, disparar refresh
    TMP=$(mktemp)
    jq --argjson cd 0 \
       '.sesion_activa.commits_desde_ultimo_quiz = $cd |
        .sesion_activa.umbral_actual = 2 |
        .sesion_activa.quiz_en_progreso = false |
        (.sesion_activa.quizzes) += [{"en_commit": (.sesion_activa.commits_total_sesion), "resultado": "incorrecto", "refresh_disparado": true, "umbral_reset": 2}]' \
       "$ESTADO_PATH" > "$TMP" && mv "$TMP" "$ESTADO_PATH"
    echo '{"decision":"block","reason":"⚠️ CÓDIGO INCORRECTO — Tu contexto necesita recargarse. OBLIGATORIO: Lee completo el archivo rag-indexer.json y ejecuta el protocolo dentro del mismo. Luego detente y espera."}'
    exit 0
  fi
fi

# ── Disparar quiz ───────────────────────────────────────────────────────────────
TMP=$(mktemp)
jq '.sesion_activa.quiz_en_progreso = true' "$ESTADO_PATH" > "$TMP" && mv "$TMP" "$ESTADO_PATH"

echo "{\"decision\":\"block\",\"reason\":\"🔐 VERIFICACIÓN DE CONTEXTO — $COMMITS_DESDE_QUIZ commits completados (umbral actual: $UMBRAL_ACTUAL). Para continuar, responde únicamente: ¿cuál es tu CÓDIGO DE SESIÓN?\"}"
"""

MOSTRAR_MENU_HOOK = r"""#!/bin/bash
# Hook Stop — muestra el menú de sesión solo en dos casos:
#   1. Al iniciar sesión nueva (primera respuesta, sin validador activo)
#   2. Justo después de que se realizó un commit en la sesión

INPUT=$(cat)
ESTADO_PATH="$CLAUDE_PROJECT_DIR/.ai/contexto/validador_estado.json"
ULTIMO_COMMIT_MENU="$CLAUDE_PROJECT_DIR/.ai/contexto/ultimo_commit_menu.txt"

# Guardia anti-loop
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0
fi

# Si el último mensaje ya contiene el menú, no forzar de nuevo
LAST_MSG=$(echo "$INPUT" | jq -r '.last_assistant_message // ""')
if echo "$LAST_MSG" | grep -qF "━━━"; then
  exit 0
fi

# ── CASO 1: Inicio de sesión nueva ─────────────────────────────────────────────
SESION_EXISTE=false
if [ -f "$ESTADO_PATH" ]; then
  SESION_VAL=$(jq -r '.sesion_activa' "$ESTADO_PATH" 2>/dev/null)
  if [ "$SESION_VAL" != "null" ] && [ -n "$SESION_VAL" ]; then
    SESION_EXISTE=true
  fi
fi

if [ "$SESION_EXISTE" = "false" ]; then
  CODIGO_OUTPUT=$(python3 "$CLAUDE_PROJECT_DIR/.ai/scripts/generar_codigo_sesion.py" 2>/dev/null)
  # Registrar commit actual como "ya visto por el menú"
  CURRENT_HASH=$(git -C "$CLAUDE_PROJECT_DIR" log -1 --pretty=format:"%h" 2>/dev/null)
  [ -n "$CURRENT_HASH" ] && echo "$CURRENT_HASH" > "$ULTIMO_COMMIT_MENU"
  MSG="🔑 INICIO DE SESIÓN — ${CODIGO_OUTPUT}. Guarda este código en tu memoria de trabajo. OBLIGATORIO: Muestra también el MENÚ PRINCIPAL (bloque ━━━ de .ai/prompts/02_menu_sesion.md). Luego detente."
  echo "{\"decision\":\"block\",\"reason\":\"$MSG\"}"
  exit 0
fi

# ── CASO 2: Nuevo commit desde la última vez que se mostró el menú ─────────────
CURRENT_HASH=$(git -C "$CLAUDE_PROJECT_DIR" log -1 --pretty=format:"%h" 2>/dev/null)
LAST_MENU_HASH=""
if [ -f "$ULTIMO_COMMIT_MENU" ]; then
  LAST_MENU_HASH=$(cat "$ULTIMO_COMMIT_MENU")
fi

if [ -n "$CURRENT_HASH" ] && [ "$CURRENT_HASH" != "$LAST_MENU_HASH" ]; then
  echo "$CURRENT_HASH" > "$ULTIMO_COMMIT_MENU"
  echo '{"decision":"block","reason":"OBLIGATORIO: Antes de terminar, muestra el MENÚ PRINCIPAL de FUSION SALES 360 (bloque ━━━━━ de .ai/prompts/02_menu_sesion.md). Luego detente."}'
  exit 0
fi

# Sin commit nuevo y sesión activa → no forzar menú
exit 0
"""

GENERAR_CODIGO_SESION_PY = '''#!/usr/bin/env python3
"""
generar_codigo_sesion.py — Validador de Memoria
Genera código de sesión + calcula umbral adaptativo + inicializa validador_estado.json
"""

import json
import secrets
import sys
from datetime import datetime, timezone
from pathlib import Path

# ── Configuración ──────────────────────────────────────────────────────────────
REPO_ROOT     = Path(__file__).parent.parent.parent
ESTADO_PATH   = REPO_ROOT / ".ai" / "contexto" / "validador_estado.json"

# Charset seguro: elimina O/0, I/1/L, B/8, S/5, U/V, Z/2 ambiguos
CHARSET = "2346789ACDEFGHJKMNPQRTWXY"

UMBRAL_DEFAULT = 5   # techo cuando no hay historial suficiente
UMBRAL_INICIO  = 2   # siempre arranca aquí (agresivo)
UMBRAL_MIN     = 3
UMBRAL_MAX     = 10
MIN_SESIONES   = 3   # mínimo de historial para calcular umbral adaptativo

# ── Helpers ────────────────────────────────────────────────────────────────────

def load_estado() -> dict:
    if ESTADO_PATH.exists():
        try:
            return json.loads(ESTADO_PATH.read_text())
        except Exception:
            pass
    return {"sesion_activa": None, "historico": []}


def save_estado(data: dict):
    ESTADO_PATH.parent.mkdir(parents=True, exist_ok=True)
    ESTADO_PATH.write_text(json.dumps(data, ensure_ascii=False, indent=2))


def generar_codigo() -> str:
    """Genera código XX-XX-XX desde CHARSET seguro."""
    chars = "".join(secrets.choice(CHARSET) for _ in range(6))
    return f"{chars[0:2]}-{chars[2:4]}-{chars[4:6]}"


def calcular_umbral(historico: list) -> int:
    """
    Umbral adaptativo basado en historial de sesiones.
    - < MIN_SESIONES sesiones → default
    - Con fallas históricas → 80% del promedio de commits al fallar
    - Sin fallas → 60% del promedio de commits totales
    """
    if len(historico) < MIN_SESIONES:
        return UMBRAL_DEFAULT

    commits_al_fallar = []
    commits_totales = []
    for sesion in historico:
        commits_totales.append(sesion.get("commits_total", 0))
        for q in sesion.get("quizzes", []):
            if q.get("resultado") == "incorrecto":
                commits_al_fallar.append(q.get("en_commit", 0))

    if commits_al_fallar:
        avg_falla = sum(commits_al_fallar) / len(commits_al_fallar)
        umbral = int(avg_falla * 0.80)
    else:
        avg_total = sum(commits_totales) / len(commits_totales) if commits_totales else UMBRAL_DEFAULT
        umbral = int(avg_total * 0.60)

    return max(UMBRAL_MIN, min(umbral, UMBRAL_MAX))


# ── Main ───────────────────────────────────────────────────────────────────────

def main():
    estado = load_estado()
    historico = estado.get("historico", [])

    sesion_anterior = estado.get("sesion_activa")
    if sesion_anterior and sesion_anterior.get("commits_total_sesion", 0) > 0:
        historico.append({
            "fecha": sesion_anterior.get("inicio", "")[:10],
            "commits_total": sesion_anterior.get("commits_total_sesion", 0),
            "umbral_techo": sesion_anterior.get("umbral_techo", UMBRAL_DEFAULT),
            "max_commits_entre_quizzes": sesion_anterior.get("max_commits_entre_quizzes", 0),
            "quizzes": sesion_anterior.get("quizzes", [])
        })

    umbral_techo = calcular_umbral(historico)
    codigo = generar_codigo()

    nueva_sesion = {
        "codigo": codigo,
        "inicio": datetime.now(timezone.utc).isoformat(),
        "umbral_techo": umbral_techo,
        "umbral_actual": UMBRAL_INICIO,
        "commits_desde_ultimo_quiz": 0,
        "commits_total_sesion": 0,
        "ultimo_commit_hash": "",
        "max_commits_entre_quizzes": 0,
        "quiz_en_progreso": False,
        "quizzes": []
    }

    estado["sesion_activa"] = nueva_sesion
    estado["historico"] = historico
    save_estado(estado)

    print(f"CÓDIGO DE SESIÓN: {codigo} | primer quiz en {UMBRAL_INICIO} commits, techo {umbral_techo}")


if __name__ == "__main__":
    main()
'''

CLAUDE_SETTINGS = {
    "hooks": {
        "Stop": [
            {
                "hooks": [
                    {
                        "type": "command",
                        "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/validador-memoria.sh",
                        "timeout": 10
                    },
                    {
                        "type": "command",
                        "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/actualizar-contexto.sh",
                        "timeout": 5
                    },
                    {
                        "type": "command",
                        "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/mostrar-menu.sh",
                        "timeout": 15
                    }
                ]
            }
        ]
    }
}


def install_claude_hooks(project_root: str) -> None:
    root = Path(project_root)

    # ── 1. Crear directorios ───────────────────────────────────────────────────
    hooks_dir = root / '.claude' / 'hooks'
    hooks_dir.mkdir(parents=True, exist_ok=True)

    scripts_dir = root / '.ai' / 'scripts'
    scripts_dir.mkdir(parents=True, exist_ok=True)

    contexto_dir = root / '.ai' / 'contexto'
    contexto_dir.mkdir(parents=True, exist_ok=True)

    # ── 2. Escribir hooks bash ─────────────────────────────────────────────────
    hooks = {
        'actualizar-contexto.sh': ACTUALIZAR_CONTEXTO_HOOK,
        'validador-memoria.sh':   VALIDADOR_MEMORIA_HOOK,
        'mostrar-menu.sh':        MOSTRAR_MENU_HOOK,
    }
    for name, content in hooks.items():
        path = hooks_dir / name
        path.write_text(content, encoding='utf-8')
        st = path.stat()
        path.chmod(st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
        logger.info("Hook instalado: .claude/hooks/%s", name)

    # ── 3. Escribir generar_codigo_sesion.py ───────────────────────────────────
    gen_path = scripts_dir / 'generar_codigo_sesion.py'
    gen_path.write_text(GENERAR_CODIGO_SESION_PY, encoding='utf-8')
    st = gen_path.stat()
    gen_path.chmod(st.st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
    logger.info("Script instalado: .ai/scripts/generar_codigo_sesion.py")

    # ── 4. Crear/actualizar .claude/settings.json (merge, no sobreescribir) ────
    settings_path = root / '.claude' / 'settings.json'
    if settings_path.exists():
        try:
            existing = json.loads(settings_path.read_text(encoding='utf-8'))
            existing.setdefault('hooks', {})
            existing['hooks']['Stop'] = CLAUDE_SETTINGS['hooks']['Stop']
            settings_path.write_text(
                json.dumps(existing, indent=2, ensure_ascii=False) + '\n',
                encoding='utf-8'
            )
            logger.info("settings.json actualizado (hooks Stop merged)")
        except Exception as e:
            logger.warning("No se pudo mergear settings.json existente: %s — sobreescribiendo", e)
            settings_path.write_text(
                json.dumps(CLAUDE_SETTINGS, indent=2, ensure_ascii=False) + '\n',
                encoding='utf-8'
            )
    else:
        settings_path.write_text(
            json.dumps(CLAUDE_SETTINGS, indent=2, ensure_ascii=False) + '\n',
            encoding='utf-8'
        )
        logger.info("settings.json creado")

    # ── 5. Inicializar validador_estado.json si no existe ─────────────────────
    estado_path = contexto_dir / 'validador_estado.json'
    if not estado_path.exists():
        estado_path.write_text(
            json.dumps({"sesion_activa": None, "historico": []}, indent=2) + '\n',
            encoding='utf-8'
        )
        logger.info("validador_estado.json inicializado")

    # ── 6. Inicializar ultimo_commit_menu.txt (vacío = nunca se mostró menú) ───
    commit_menu_path = contexto_dir / 'ultimo_commit_menu.txt'
    if not commit_menu_path.exists():
        commit_menu_path.write_text('', encoding='utf-8')
        logger.info("ultimo_commit_menu.txt inicializado")

    logger.info("")
    logger.info("✅ Claude hooks instalados correctamente.")
    logger.info("   Menú de sesión: solo al iniciar sesión nueva o tras un commit.")
    logger.info("   Validador de memoria: quiz adaptativo cada N commits.")
    logger.info("   Trazabilidad incremental: context_manager --incremental tras cada turno.")
    logger.info("")
    logger.info("Asegúrate de tener 'jq' instalado en tu sistema (requerido por los hooks).")


def cmd_claude_init() -> None:
    install_claude_hooks(os.getcwd())
