# API GoLoco (sola lettura)

API JSON in PHP puro per leggere `app.sqlite`. Versione corrente: `v1`.

## Installazione

Requisiti: PHP 8.0+, estensioni `pdo_sqlite` e `mbstring`, Apache con `mod_rewrite`. La directory `api/` deve essere servita dal web server; `app.sqlite` resta fuori dalla directory pubblica dell'API. Il codice apre SQLite e abilita `PRAGMA query_only`, quindi ogni scrittura viene rifiutata anche in caso di errore applicativo.

Base URL di esempio: `https://goloco.it/api/v1`.

Se `mod_rewrite` non è disponibile, usare `api/index.php?route=v1/pois` e aggiungere gli altri parametri normalmente. In produzione usare HTTPS. Per chiamate da una web app, inserire le origini ammesse in `api/config.php`; le app native Android/iOS non necessitano di CORS.

## Risposta e paginazione

Le liste restituiscono:

```json
{
  "data": [],
  "meta": { "limit": 20, "offset": 0, "count": 0, "next_offset": null }
}
```

- `limit`: 1-100, default 20.
- `offset`: 0-1.000.000, default 0.
- `next_offset`: valore da inviare nella richiesta successiva; `null` indica che non risultano altre righe.

Gli errori hanno forma `{"error":{"code":"invalid_parameter","message":"..."}}` e usano status HTTP coerenti (`404`, `405`, `422`, `500`).

## Filtri comuni

- `city`: nome esatto del comune, senza distinzione tra maiuscole/minuscole.
- `cap`: CAP italiano di esattamente 5 cifre.
- `lat` e `lon`: coordinate decimali, da fornire insieme.
- `radius_km`: raggio dalle coordinate, maggiore di 0 e massimo 50 km; default 5 km.
- `category`: categoria disponibile per POI e food. Accetta sia il nome semplice (`restaurant`) sia il valore OpenStreetMap completo (`amenity:restaurant`).

I filtri possono essere combinati: per esempio città + categoria, oppure CAP + coordinate. Quando si usano le coordinate, ogni elemento contiene `distance_km` ed è ordinato dal più vicino.

Per POI e food, `city` e `cap` si riferiscono al comune associato in `comuni`; l'indirizzo testuale della singola attività rimane nel campo `address`.

Nelle risposte di POI e food, la categoria viene normalizzata senza perdere il dato originale:

```json
{
  "category": "restaurant",
  "category_group": "amenity",
  "raw_category": "amenity:restaurant"
}
```

## Endpoint

### Stato

`GET /api/v1/health`

### Comuni

`GET /api/v1/comuni`

`GET /api/v1/comuni/{id}`

Esempi:

```text
GET /api/v1/comuni?city=Roma
GET /api/v1/comuni?cap=00100&limit=10
GET /api/v1/comuni?lat=41.9028&lon=12.4964&radius_km=20
```

### Punti di interesse

`GET /api/v1/pois`

`GET /api/v1/pois/{id}`

Esempi:

```text
GET /api/v1/pois?city=Firenze&limit=25&offset=0
GET /api/v1/pois?cap=20121&category=museum
GET /api/v1/pois?lat=45.4642&lon=9.1900&radius_km=3&limit=50
```

### Food & beverage

`GET /api/v1/food`

`GET /api/v1/food/{id}`

Esempio:

```text
GET /api/v1/food?city=Lecce&category=restaurant&limit=50
```

### Food vicino a un POI

Usa le distanze già calcolate nella tabella `poi_nearby_food`:

`GET /api/v1/pois/{poi_id}/nearby-food?limit=20&offset=0`

La risposta include `distance_meters` ed è ordinata per distanza crescente.

## Esempi mobile

Kotlin (Android):

```kotlin
val url = "https://goloco.it/api/v1/pois?lat=41.9028&lon=12.4964&radius_km=5&limit=20"
val request = Request.Builder().url(url).get().build()
```

Swift (iOS):

```swift
var components = URLComponents(string: "https://goloco.it/api/v1/food")!
components.queryItems = [
    URLQueryItem(name: "city", value: "Roma"),
    URLQueryItem(name: "limit", value: "20")
]
URLSession.shared.dataTask(with: components.url!).resume()
```

## Sicurezza e operatività

- Sono accettati soltanto `GET` e `OPTIONS`.
- Tutti i valori entrano nelle query tramite prepared statement; tabella, campi e ordinamento sono definiti nel codice.
- Il database è protetto anche con `PRAGMA query_only`, gli errori SQL non vengono esposti al client e il listing delle directory è disabilitato.
- In produzione configurare HTTPS, log rotation, backup del database e rate limiting sul reverse proxy/web server (per esempio nginx, Apache o Cloudflare).
- Il filtro geografico usa prima un bounding box e poi la distanza Haversine. Per carichi elevati conviene aggiungere indici `(lat, lon)` a `comuni`, `pois` e `food_beverage` durante una finestra di manutenzione.
