# Búsqueda pública de estaciones

## Propósito

Este flujo expone el listado público de estaciones priorizando la búsqueda por cercanía usando la geolocalización del navegador.

El diseño actual persigue estos objetivos:

- dejar la ubicación actual como acción principal de la pantalla
- mantener la búsqueda manual como alternativa secundaria
- validar el query string antes de ejecutar filtros o cálculos de distancia
- minimizar columnas y relaciones para no sobrecargar la consulta pública

## Endpoint

`GET /endpoint/estaciones`

## Permisos y roles

No requiere autenticación.

La geolocalización depende del permiso que la persona usuaria otorgue en su navegador. El backend no persiste coordenadas del dispositivo; sólo usa las recibidas en el request actual para ordenar el resultado.

## Inputs

Parámetros soportados:

- `servicio_id`: opcional, entero, debe existir en `servicios`
- `departamento_id`: opcional, entero, debe existir en `departamentos`
- `ciudad_id`: opcional, entero, debe existir en `ciudades`
- `page`: opcional, entero mayor o igual a `1`
- `nearest`: opcional, booleano
- `lat`: opcional, numérico entre `-90` y `90`
- `lng`: opcional, numérico entre `-180` y `180`

Si `nearest=1`, `lat` y `lng` son obligatorios.

## Output

### Respuesta exitosa

```json
{
  "estaciones": {
    "data": [
      {
        "id": 1,
        "direccion": "Avda. Primer Presidente",
        "ciudad_id": 10,
        "ubicacion": {
          "lat": -25.25595,
          "lng": -57.57174
        },
        "distance_km": 2.4,
        "ciudad": {
          "id": 10,
          "nombre": "Asunción"
        },
        "departamento": {
          "id": 1,
          "nombre": "Capital"
        }
      }
    ],
    "current_page": 1,
    "last_page": 10,
    "total": 120
  },
  "servicios": [
    {
      "id": 3,
      "titulo": "Copemarket"
    }
  ],
  "departamentos": [
    {
      "id": 1,
      "nombre": "Capital",
      "ciudades": [
        {
          "id": 10,
          "nombre": "Asunción",
          "departamento_id": 1
        }
      ]
    }
  ]
}
```

### Error de validación

```json
{
  "message": "The given data was invalid.",
  "errors": {
    "nearest": [
      "Para ordenar por cercanía se requieren latitud y longitud."
    ]
  }
}
```

## Comportamiento de negocio

1. La pantalla pública muestra primero la acción de usar ubicación actual.
2. Si la persona acepta el permiso del navegador, el frontend envía `nearest=1`, `lat` y `lng`.
3. El backend valida el request y calcula distancia con Haversine.
4. Las estaciones se ordenan por cercanía y cada tarjeta muestra la distancia en kilómetros.
5. La búsqueda manual por ciudad o departamento sigue disponible como alternativa desplegable.

## Notas de seguridad

- El endpoint valida ids y coordenadas para evitar inputs inválidos.
- El backend no guarda la ubicación del dispositivo.
- Si falta una coordenada o el rango es inválido, responde `422`.

## Notas de performance

- La consulta pública selecciona sólo columnas necesarias de `estaciones`.
- Ya no se eager-load de `servicios` por estación porque la vista no lo usa.
- El orden por cercanía se resuelve en memoria sobre el conjunto filtrado para evitar SQL específico del motor.
- La paginación se aplica después del orden por distancia en el flujo de geolocalización.

## Cómo correr los tests relacionados

Desde `copetrol-api-and-public`:

```bash
php8.1 artisan test --filter=EstacionesEndpointTest
```
