# Referencia de API REST

Base URL: `http://localhost:3000`

## Salud del servicio

### `GET /health/live`

Confirma que el proceso Node está respondiendo. No requiere autenticación.

### `GET /health`

Comprueba el proceso y la conexión a MySQL. Devuelve `200` cuando el servicio está preparado y `503` cuando el proceso responde pero MySQL no está disponible. Las fechas `timestamp` y `started_at` están en UTC ISO 8601; `local_time` incluye la zona configurada.

Todas las rutas de API requieren autenticación (sesión activa). Las rutas de vistas redirigen a `/login` si no hay sesión.

---

## Autenticación

### `POST /auth/login`
Inicia sesión.

**Body (form-data o JSON):**
```json
{ "username": "george", "password": "4213" }
```

**Respuesta (éxito):** Redirect a `/`
**Respuesta (error):** Redirect a `/login?error=1`

### `GET /auth/logout`
Cierra sesión y redirige a `/login`.

---

## Propiedades

### `GET /api/properties`
Devuelve lista de propiedades con filtros opcionales por query string.

**Query params (todos opcionales):**

| Parámetro       | Tipo   | Descripción                           |
| --------------- | ------ | ------------------------------------- |
| `min_price`     | number | Precio mínimo                         |
| `max_price`     | number | Precio máximo                         |
| `property_type` | string | Tipo canónico (piso, chalet, casa...) |
| `municipality`  | string | Municipio                             |
| `min_rooms`     | number | Habitaciones mínimas                  |
| `min_size_m2`   | number | Superficie mínima                     |
| `portal`        | string | Fuente/portal                         |
| `page`          | number | Página (default: 1)                   |
| `limit`         | number | Resultados por página (default: 20)   |

**Respuesta 200:**
```json
{
  "properties": [ { "id": 1, "title": "...", "price": 150000, ... } ],
  "total": 342,
  "page": 1,
  "pages": 18
}
```

### `GET /api/properties/:id`
Devuelve una propiedad por ID.

**Respuesta 200:** Objeto propiedad completo.
**Respuesta 404:** `{ "error": "Not found" }`

### `PATCH /api/properties/:id/favorite`
Marca/desmarca como favorita.

**Body:**
```json
{ "is_favorite": 1 }
```

### `PATCH /api/properties/:id/notes`
Guarda notas personales.

**Body:**
```json
{ "notes": "Contactar al agente esta tarde" }
```

### `GET /api/properties/:id/price-history`
Devuelve el historial de precios de la propiedad.

**Respuesta 200:**
```json
[
  { "price": 160000, "recorded_at": "2024-01-15T10:00:00Z" },
  { "price": 150000, "recorded_at": "2024-03-01T10:00:00Z" }
]
```

---

## Scraper

### `POST /api/scrape`
Lanza el scraping de todos los portales activos de forma asíncrona.

**Respuesta 202:**
```json
{ "message": "Scraping iniciado", "jobId": "..." }
```

### `GET /api/scrape/status`
Estado del scraping en curso.

**Respuesta 200:**
```json
{
  "status": "running",
  "started_at": "2024-03-01T10:00:00Z",
  "total_found": 234,
  "new_saved": 12,
  "errors": 0
}
```

### `GET /api/scrape/logs`
Últimas ejecuciones del scraper.

**Respuesta 200:** Array de entradas de `scraping_log`.

---

## Portales

### `GET /api/portals`
Lista todos los portales.

**Respuesta 200:**
```json
[
  { "id": 1, "name": "Idealista", "slug": "idealista", "active": 1 }
]
```

### `PATCH /api/portals/:id`
Activa o desactiva un portal.

**Body:**
```json
{ "active": 0 }
```

---

## Zonas

### `GET /api/zones`
Lista todas las zonas/municipios.

### `POST /api/zones`
Crea una nueva zona.

**Body:**
```json
{
  "name": "Serra",
  "slug": "serra",
  "lat": 39.6901,
  "lng": -0.4218,
  "radius_km": 8,
  "active": 1,
  "pisos_slug": "pisos-serra"
}
```

### `PATCH /api/zones/:id`
Actualiza una zona existente (nombre, activo, radio...).

### `DELETE /api/zones/:id`
Elimina una zona (no elimina propiedades asociadas).

---

## Filtros

### `GET /api/filters`
Lista filtros guardados.

### `POST /api/filters`
Crea un filtro guardado.

**Body:**
```json
{
  "name": "Chalets grandes",
  "min_price": 100000,
  "max_price": 300000,
  "property_type": "chalet",
  "municipality": "Llíria",
  "min_rooms": 3,
  "email_alerts": 1
}
```

### `DELETE /api/filters/:id`
Elimina un filtro.

---

## Analytics

### `GET /api/analytics/summary`
Estadísticas generales.

**Respuesta 200:**
```json
{
  "total_properties": 1243,
  "avg_price": 187000,
  "avg_price_per_m2": 1250,
  "by_type": { "piso": 450, "chalet": 320, "casa": 280 },
  "by_portal": { "Idealista": 400, "Fotocasa": 350 },
  "by_municipality": { "Llíria": 600, "Bétera": 200 }
}
```

### `GET /api/analytics/price-trends`
Evolución de precios medios por mes.

---

## Mapa

### `GET /api/map/properties`
Devuelve propiedades con coordenadas para el mapa.

**Query params:** Mismos filtros que `/api/properties`.

**Respuesta 200:**
```json
[
  { "id": 1, "title": "...", "price": 150000, "lat": 39.629, "lng": -0.597, "property_type": "chalet" }
]
```
