# Regla: Estilo de Código Kotlin

Esta regla es OBLIGATORIA para todo código Kotlin nuevo o modificado.
Kotlin ofrece construcciones que eliminan verbosidad y reducen bugs por diseño.
Usarlas no es opcional: `val` sobre `var`, null safety real, y coroutines sobre callbacks.

---

## Linters obligatorios: ktlint y Detekt

- **ktlint** verifica formato y convenciones del lenguaje. Corre en CI como verificación bloqueante.
- **Detekt** analiza complejidad, code smells y anti-patrones. Falla con severidad `error`.
- Configurar el IDE para aplicar ktlint al guardar. No formatear manualmente.

```kotlin
// build.gradle.kts
plugins {
    id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
    id("io.gitlab.arturbosch.detekt") version "1.23.6"
}

detekt {
    config.setFrom("$rootDir/config/detekt.yml")
    buildUponDefaultConfig = true
}
```

---

## val sobre var: inmutabilidad por defecto

Declarar `val` siempre que el valor no cambie después de la asignación.
`var` solo cuando la mutabilidad es estrictamente necesaria y documentada.

```kotlin
// MAL — var donde el valor nunca se reasigna
var nombre = "Juan"
var total = calcularTotal(items)

// BIEN — val comunica que no cambia
val nombre = "Juan"
val total = calcularTotal(items)
```

---

## data class para DTOs, sealed class para estados

```kotlin
// BIEN — data class para transportar datos (equals, hashCode, copy gratis)
data class UsuarioDto(
    val id: UUID,
    val nombre: String,
    val email: String,
)

// BIEN — sealed class para estados cerrados con when exhaustivo
sealed class EstadoPedido {
    data class Procesando(val progreso: Int) : EstadoPedido()
    data class Completado(val transaccionId: String) : EstadoPedido()
    data class Error(val mensaje: String) : EstadoPedido()
}

// MAL — String o constantes enteras para estados
const val ESTADO_PROCESANDO = 1
const val ESTADO_COMPLETADO = 2
```

---

## Null safety: nunca !! en producción

El operador `!!` es una bomba de tiempo. Lanza `NullPointerException` en runtime.
Usar `?.`, `?:`, `let`, o `requireNotNull` con mensaje descriptivo.

```kotlin
// MAL — !! puede lanzar NPE en producción
val nombre = usuario!!.perfil!!.nombre

// BIEN — manejo explícito del nulo
val nombre = usuario?.perfil?.nombre ?: "Anónimo"

// BIEN — fallo temprano con mensaje claro cuando el nulo es inesperado
val usuario = repositorio.buscarPorId(id)
    ?: throw IllegalStateException("Usuario $id no encontrado")
```

Detekt con la regla `ForbiddenCode` o `NullableToStringCall` debe detectar `!!` en `src/`.

---

## when exhaustivo para sealed classes

El compilador verifica que todos los casos están cubiertos cuando `when` es expresión.

```kotlin
// MAL — when como sentencia pierde exhaustividad del compilador
when (estado) {
    is EstadoPedido.Procesando -> mostrarProgreso(estado.progreso)
    is EstadoPedido.Completado -> mostrarExito()
    // Error olvidado — el compilador no avisa
}

// BIEN — when como expresion fuerza exhaustividad
val mensaje: String = when (estado) {
    is EstadoPedido.Procesando -> "Procesando ${estado.progreso}%"
    is EstadoPedido.Completado -> "Completado: ${estado.transaccionId}"
    is EstadoPedido.Error -> "Error: ${estado.mensaje}"
}
```

---

## Extension functions para enriquecer tipos sin herencia

```kotlin
// MAL — función utilitaria con receptor como parámetro
fun formatearFecha(fecha: LocalDate): String = ...

// BIEN — extension function: legible como llamada a método
fun LocalDate.formatearParaFactura(): String =
    DateTimeFormatter.ofPattern("dd/MM/yyyy").format(this)

// Uso natural
val texto = fechaEmision.formatearParaFactura()
```

---

## Scope functions con criterio

| Función | Uso correcto |
|---------|-------------|
| `let` | Transformar un valor nullable o encadenar operaciones |
| `run` | Ejecutar bloque en contexto del objeto, retornar resultado |
| `with` | Operar sobre objeto sin repetir su nombre (no nullable) |
| `apply` | Configurar objeto y retornarlo (builders) |
| `also` | Efecto secundario (logging, validación) sin alterar el flujo |

```kotlin
// BIEN — apply para configurar
val intent = Intent(context, MainActivity::class.java).apply {
    putExtra("userId", usuario.id)
    flags = Intent.FLAG_ACTIVITY_CLEAR_TOP
}

// MAL — anidamiento de scope functions (ilegible)
usuario?.let { u ->
    u.perfil?.run {
        with(this) { /* confusión total sobre 'this' */ }
    }
}
```

Máximo dos scope functions anidadas. Si se necesitan más, extraer funciones con nombre.

---

## Coroutines: suspend funciones, no callbacks

```kotlin
// MAL — callback hell
fun obtenerUsuario(id: String, callback: (Usuario?, Error?) -> Unit) {
    api.buscar(id) { resultado, error ->
        if (error != null) callback(null, error)
        else callback(resultado, null)
    }
}

// BIEN — suspend function: lineal, testeable, componible
suspend fun obtenerUsuario(id: String): Usuario =
    api.buscar(id)
```

---

## Nombres y límites de tamaño

- Clases e interfaces: `PascalCase`
- Funciones, variables y propiedades: `camelCase`
- Constantes (`val` en companion object o top-level): `SCREAMING_CASE`
- Máximo **40 líneas** por función (sin contar líneas en blanco y comentarios)
- Máximo **4 parámetros** por función. Si se necesitan más: data class de parámetros
- Single expression functions cuando el cuerpo es una sola expresión legible

```kotlin
// BIEN — single expression function
fun esAdmin(usuario: Usuario): Boolean = usuario.rol == Rol.ADMIN

// MAL — cuerpo innecesariamente expandido
fun esAdmin(usuario: Usuario): Boolean {
    return usuario.rol == Rol.ADMIN
}
```

---

## Checklist de estilo Kotlin antes de abrir PR

- [ ] ktlint aplicado sin errores
- [ ] Detekt pasa con configuración del proyecto
- [ ] Sin `!!` en código de `src/` (solo en tests con justificación)
- [ ] `val` usado por defecto; `var` solo donde hay mutacion real
- [ ] `data class` para DTOs, `sealed class` para estados cerrados
- [ ] `when` sobre sealed classes es expresion (no sentencia)
- [ ] Funciones <= 40 líneas
- [ ] Sin callbacks: usar `suspend` functions y coroutines
- [ ] Sin scope functions anidadas más de dos niveles
