# Regla: Hooks de Calidad para Kotlin

Esta regla define las verificaciones automáticas obligatorias que corren en cada
commit y merge para código Kotlin. Los hooks detectan errores en segundos —
mucho antes de que lleguen a code review o a producción.

---

## ktlint --format antes de commit (pre-commit hook)

ktlint verifica y corrige formato automáticamente. El hook debe correr en modo
`--format` para aplicar correcciones, y fallar si quedan errores no autocorregibles.

```bash
#!/bin/sh
# .git/hooks/pre-commit o via lefthook/husky

echo "Ejecutando ktlint..."
./gradlew ktlintFormat

# Verificar si ktlint introdujo cambios (reformateo)
if ! git diff --quiet; then
    echo "ktlint reformateó archivos. Revisa los cambios y vuelve a hacer commit."
    exit 1
fi

./gradlew ktlintCheck
if [ $? -ne 0 ]; then
    echo "ktlint encontró errores de formato no corregibles automáticamente."
    exit 1
fi
```

Configuración recomendada vía **Lefthook** (más rápido que husky para JVM):

```yaml
# lefthook.yml
pre-commit:
  parallel: false
  commands:
    ktlint:
      glob: "*.{kt,kts}"
      run: ./gradlew ktlintFormat && ./gradlew ktlintCheck
```

---

## Detekt con configuración del proyecto

Detekt analiza complejidad ciclomática, código duplicado, funciones largas y
anti-patrones. La configuración base del proyecto define la severidad de cada regla.

```yaml
# config/detekt.yml — reglas críticas en error
complexity:
  LongMethod:
    active: true
    threshold: 40
  ComplexMethod:
    active: true
    threshold: 10

style:
  ForbiddenComment:
    active: true
    values: ['TODO:', 'FIXME:', 'HACK:']

potential-bugs:
  UnsafeCallOnNullableType:
    active: true  # detecta !!
```

```bash
# En el hook pre-merge (no pre-commit — Detekt es más lento)
./gradlew detekt
if [ $? -ne 0 ]; then
    echo "Detekt encontró problemas. Revisa el reporte en build/reports/detekt/"
    exit 1
fi
```

---

## Gradle build antes de merge (CI obligatorio)

El build completo con tests corre en CI antes de aprobar el merge.
Localmente, correr al menos una vez antes de abrir el PR.

```bash
# Pre-push hook — más lento pero evita PRs rotos
./gradlew build
if [ $? -ne 0 ]; then
    echo "Build fallido. No se puede hacer push."
    exit 1
fi
```

En CI (GitHub Actions):
```yaml
- name: Build y test
  run: ./gradlew build koverXmlReport

- name: Verificar cobertura
  run: ./gradlew koverVerify
```

---

## Detección de println en código no-test

`println()` en producción contamina los logs del sistema y puede exponer datos sensibles.
Solo se permite en archivos bajo `src/test/`.

```bash
# Script de detección — corre en pre-commit
KOTLIN_SRC=$(git diff --cached --name-only | grep -E "^src/main.*\.kt$")

if [ -n "$KOTLIN_SRC" ]; then
    MATCHES=$(echo "$KOTLIN_SRC" | xargs grep -l "println(" 2>/dev/null)
    if [ -n "$MATCHES" ]; then
        echo "ERROR: println() encontrado en código de producción:"
        echo "$MATCHES"
        echo "Usar Timber.d() o el logger del proyecto."
        exit 1
    fi
fi
```

Alternativa vía regla Detekt personalizada o `ForbiddenMethodCall`:
```yaml
style:
  ForbiddenMethodCall:
    active: true
    methods:
      - reason: "Usar Timber o logger del proyecto"
        value: "kotlin.io.println"
```

---

## Detección de !! (force unwrap) en src/

El operador `!!` en producción es una bomba de tiempo. Detectarlo en pre-commit
evita que llegue al repositorio principal.

```bash
# Detección de !! en archivos Kotlin de producción
KOTLIN_MAIN=$(git diff --cached --name-only | grep -E "^src/main.*\.kt$")

if [ -n "$KOTLIN_MAIN" ]; then
    # Excluir !! dentro de comentarios y strings
    MATCHES=$(echo "$KOTLIN_MAIN" | xargs grep -En "[^!'\"]\!\![^=]" 2>/dev/null | grep -v "//.*!!")
    if [ -n "$MATCHES" ]; then
        echo "ERROR: Operador !! encontrado en src/main:"
        echo "$MATCHES"
        echo "Usar ?., ?: o requireNotNull() con mensaje descriptivo."
        exit 1
    fi
fi

```

---

## Android Lint con severidad error

Android Lint detecta problemas específicos de Android: uso de APIs deprecadas,
faltantes de permisos, problemas de rendimiento en Compose, etc.

```kotlin
// build.gradle.kts
android {
    lint {
        abortOnError = true
        warningsAsErrors = false  // solo errors bloquean el build
        checkDependencies = true
        htmlReport = true
        xmlReport = true
    }
}
```

Reglas de lint que DEBEN estar en severidad `error` en el proyecto:
- `HardcodedText` — textos sin localizar
- `UnusedResources` — recursos no referenciados
- `MissingPermission` — llamadas a APIs que requieren permisos no declarados
- `NewApi` — uso de API no disponible en el minSdk del proyecto

```bash
# En pre-merge (CI)
./gradlew lint
if [ $? -ne 0 ]; then
    echo "Android Lint encontró errores. Ver app/build/reports/lint-results.html"
    exit 1
fi
```

---

## Configuración de Lefthook (recomendado)

Lefthook es más rápido que husky y nativo en JVM. Configuración completa:

```yaml
# lefthook.yml en la raíz del proyecto
pre-commit:
  parallel: true
  commands:
    ktlint:
      glob: "src/main/**/*.{kt,kts}"
      run: ./gradlew ktlintFormat ktlintCheck
    detectar-println:
      glob: "src/main/**/*.kt"
      run: |
        if grep -rn "println(" {staged_files} 2>/dev/null; then
          echo "ERROR: println() en producción. Usar el logger del proyecto."
          exit 1
        fi
    detectar-force-unwrap:
      glob: "src/main/**/*.kt"
      run: |
        if grep -En "[^!'\"]\!\![^=]" {staged_files} 2>/dev/null | grep -v "//"; then
          echo "ERROR: Operador !! en producción."
          exit 1
        fi

pre-push:
  commands:
    detekt:
      run: ./gradlew detekt
    lint:
      run: ./gradlew lint
```

---

## Checklist de hooks Kotlin

- [ ] ktlint configurado y corriendo en pre-commit
- [ ] Detekt configurado con `config/detekt.yml` del proyecto
- [ ] Gradle build corre en CI antes de aprobar merge
- [ ] Detección de `println()` activa para `src/main/`
- [ ] Detección de `!!` activa para `src/main/`
- [ ] Android Lint con `abortOnError = true`
- [ ] Lefthook o husky instalado y versionado en el repositorio
