# Catálogo de ciudades y aliases

## Propósito

Este módulo permite administrar:

- el catálogo oficial de ciudades normalizadas
- los aliases operativos usados por el importador de estaciones

El objetivo es sacar del código las equivalencias manuales de ciudades y dejar
la resolución de nombres alternativos como dato administrable en base de datos.

## Endpoints

### Ciudades

- `GET /api/admin/ciudades`
- `POST /api/admin/ciudades`
- `GET /api/admin/ciudades/{id}/edit`
- `PUT /api/admin/ciudades/{id}`
- `DELETE /api/admin/ciudades/{id}`

### Aliases de ciudades

- `GET /api/admin/ciudades-aliases`
- `POST /api/admin/ciudades-aliases`
- `GET /api/admin/ciudades-aliases/{id}/edit`
- `PUT /api/admin/ciudades-aliases/{id}`
- `DELETE /api/admin/ciudades-aliases/{id}`

### Listas auxiliares

- `GET /api/admin/list/cities`
- `GET /api/admin/list/departamentos`

## Permisos y roles

Todos los endpoints anteriores requieren:

- `auth:sanctum`
- `abilities:admin`
- `uuid-check`
- `rootUser`

Esto evita que un usuario autenticado sin privilegios root altere el catálogo
base que usa el importador y el resto del sistema.

## Inputs

### Crear o actualizar ciudad

```json
{
  "nombre": "Tomás Romero Pereira",
  "departamento_id": 7,
  "latitud": -26.483,
  "longitud": -55.250
}
```

Reglas principales:

- `nombre`: requerido, 2 a 150 caracteres, único dentro del departamento
- `departamento_id`: requerido, debe existir en `departamentos`
- `latitud`: requerida, rango `-90` a `90`
- `longitud`: requerida, rango `-180` a `180`

### Crear o actualizar alias

```json
{
  "nombre": "María Auxiliadora",
  "ciudad_id": 245
}
```

Reglas principales:

- `nombre`: requerido, 2 a 150 caracteres, único en `ciudades_aliases`
- `ciudad_id`: requerido, debe existir en `ciudades`

## Outputs

### Respuesta exitosa de listado

```json
{
  "data": [
    {
      "id": 1,
      "nombre": "Santaní",
      "slug": "santani",
      "ciudad_id": 15,
      "ciudad": "San Estanislao",
      "departamento": "San Pedro"
    }
  ]
}
```

### Respuesta exitosa de alta o edición

```json
{
  "type": "ciudades_aliases",
  "id": "1",
  "attributes": {
    "id": 1,
    "nombre": "Santaní",
    "slug": "santani",
    "ciudad_id": 15
  }
}
```

### Error de validación

```json
{
  "message": "The given data was invalid.",
  "errors": {
    "ciudad_id": [
      "The selected ciudad id is invalid."
    ]
  }
}
```

## Comportamiento de negocio

1. Las ciudades representan el nombre oficial y único que debe usar el sistema.
2. Los aliases almacenan variantes operativas o comerciales, por ejemplo:
   - `Santaní` -> `San Estanislao`
   - `Campo 9` -> `Doctor J. Eulogio Estigarribia`
   - `María Auxiliadora` -> `Tomás Romero Pereira`
3. El importador consulta primero ciudades oficiales y luego aliases, todo desde base de datos.
4. Al eliminar una ciudad, sus aliases se eliminan por `cascade` para no dejar referencias huérfanas.

## Notas de seguridad

- Los requests validan ids existentes para evitar relaciones inválidas.
- El CRUD corre bajo `rootUser` para reducir riesgo de manipulación del catálogo base.
- El importador ya no depende de aliases hardcodeados, lo que evita divergencia entre ambientes.

## Notas de performance

- Los listados usan Query Builder y columnas explícitas.
- Los aliases se resuelven con un lookup compacto en memoria durante la importación.
- El endpoint de departamentos sólo expone `id` y `nombre`, que es lo mínimo necesario para los selectores.

## Cómo correr los tests relacionados

Desde `copetrol-api-and-public`:

```bash
php8.1 artisan test --filter=CiudadesAdminTest
php8.1 artisan test --filter=EstacionesImportServiceTest
```
