# API Reference

Complete reference for the WineBox REST API.

## Base URL

```
http://localhost:8000/api
```

## Authentication

All API endpoints (except `/health`) require JWT authentication.

### Getting a Token

```bash
# Note: OAuth2 spec uses 'username' field, but WineBox expects email
curl -X POST http://localhost:8000/api/auth/token \
  -d "username=myemail@example.com&password=mypass"
```

**Response**:
```json
{
  "access_token": "eyJ...",
  "token_type": "bearer"
}
```

### Using the Token

Include the token in the `Authorization` header for all requests:

```bash
curl -H "Authorization: Bearer <your-token>" \
  http://localhost:8000/api/wines
```

### Token Expiration

Tokens expire after 30 minutes. Request a new token when needed.

## Endpoints

### Health Check

#### GET /health

Check if the server is running.

**Response**:
```json
{
  "status": "healthy",
  "version": "0.5.12",
  "app_name": "WineBox"
}
```

---

## Wine Endpoints

### POST /api/wines/checkin

Check in wine bottles to the cellar.

**Content-Type**: `multipart/form-data`

**Parameters**:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| front_label | file | Yes | Front label image |
| back_label | file | No | Back label image |
| name | string | No | Wine name (auto-detected if not provided) |
| winery | string | No | Winery name |
| vintage | integer | No | Vintage year (1900-2100) |
| grape_variety | string | No | Grape variety |
| region | string | No | Wine region (e.g., Burgundy, Napa Valley) |
| sub_region | string | No | Sub-region (e.g., Côte de Nuits, Médoc) |
| appellation | string | No | Appellation (e.g., Nuits-St-Georges, Pomerol) |
| classification | string | No | Classification (e.g., Grand Cru, DOCG, Reserve) |
| country | string | No | Country of origin |
| alcohol_percentage | float | No | Alcohol percentage (0-100) |
| quantity | integer | Yes | Number of bottles (min: 1) |
| notes | string | No | Check-in notes |

**Response**: `201 Created`
```json
{
  "id": "uuid",
  "name": "Wine Name",
  "winery": "Winery Name",
  "vintage": 2019,
  "grape_variety": "Cabernet Sauvignon",
  "region": "Burgundy",
  "sub_region": "Côte de Nuits",
  "appellation": "Nuits-St-Georges",
  "country": "France",
  "classification": "Premier Cru",
  "alcohol_percentage": 14.5,
  "front_label_text": "OCR extracted text...",
  "back_label_text": null,
  "front_label_image_path": "uuid.jpg",
  "back_label_image_path": null,
  "created_at": "2024-01-15T10:30:00Z",
  "updated_at": "2024-01-15T10:30:00Z",
  "inventory": {
    "quantity": 6,
    "updated_at": "2024-01-15T10:30:00Z"
  }
}
```

---

### POST /api/wines/{wine_id}/checkout

Check out wine bottles from the cellar.

**Content-Type**: `multipart/form-data`

**Path Parameters**:
- `wine_id`: UUID of the wine

**Form Parameters**:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| quantity | integer | Yes | Number of bottles to remove (min: 1) |
| notes | string | No | Check-out notes |

**Response**: `200 OK`

Returns the updated wine object.

**Errors**:
- `404 Not Found`: Wine not found
- `400 Bad Request`: Not enough bottles in stock

---

### GET /api/wines

List all wines.

**Query Parameters**:

| Name | Type | Description |
|------|------|-------------|
| skip | integer | Number of records to skip (default: 0) |
| limit | integer | Maximum records to return (default: 100) |
| in_stock | boolean | Filter by stock status |

**Response**: `200 OK`
```json
[
  {
    "id": "uuid",
    "name": "Wine Name",
    ...
    "inventory": {
      "quantity": 3,
      "updated_at": "..."
    }
  }
]
```

---

### GET /api/wines/{wine_id}

Get wine details with full transaction history.

**Response**: `200 OK`
```json
{
  "id": "uuid",
  "name": "Wine Name",
  ...
  "inventory": {...},
  "transactions": [
    {
      "id": "uuid",
      "transaction_type": "CHECK_IN",
      "quantity": 6,
      "notes": "Purchased at auction",
      "transaction_date": "2024-01-15T10:30:00Z"
    }
  ]
}
```

---

### PUT /api/wines/{wine_id}

Update wine metadata.

**Content-Type**: `application/json`

**Request Body**:
```json
{
  "name": "Updated Name",
  "vintage": 2020,
  "grape_variety": "Merlot",
  "sub_region": "Côte de Nuits",
  "appellation": "Gevrey-Chambertin",
  "classification": "Grand Cru"
}
```

Only include fields you want to update. All fields are optional.

**Response**: `200 OK`

---

### DELETE /api/wines/{wine_id}

Delete a wine and all its history.

**Response**: `204 No Content`

---

## Cellar Endpoints

### GET /api/cellar

Get current cellar inventory (wines in stock).

**Query Parameters**:
- `skip`: Number to skip (default: 0)
- `limit`: Maximum to return (default: 100)

**Response**: `200 OK`

Returns list of wines with quantity > 0.

---

### GET /api/cellar/summary

Get cellar summary statistics.

**Response**: `200 OK`
```json
{
  "total_bottles": 42,
  "unique_wines": 15,
  "total_wines_tracked": 20,
  "by_vintage": {
    "2019": 12,
    "2020": 8
  },
  "by_country": {
    "France": 18,
    "Italy": 12
  },
  "by_grape_variety": {
    "Cabernet Sauvignon": 15,
    "Merlot": 10
  }
}
```

---

## Transaction Endpoints

### GET /api/transactions

List all transactions.

**Query Parameters**:

| Name | Type | Description |
|------|------|-------------|
| skip | integer | Number to skip |
| limit | integer | Maximum to return |
| transaction_type | string | Filter: "CHECK_IN" or "CHECK_OUT" |
| wine_id | string | Filter by wine UUID |

**Response**: `200 OK`
```json
[
  {
    "id": "uuid",
    "wine_id": "uuid",
    "transaction_type": "CHECK_IN",
    "quantity": 6,
    "notes": "...",
    "transaction_date": "2024-01-15T10:30:00Z",
    "created_at": "2024-01-15T10:30:00Z",
    "wine": {
      "id": "uuid",
      "name": "Wine Name",
      "vintage": 2019,
      "winery": "Winery Name"
    }
  }
]
```

---

### GET /api/transactions/{transaction_id}

Get a single transaction.

**Response**: `200 OK`

---

## Search Endpoint

### GET /api/search

Search wines by various criteria.

**Query Parameters**:

| Name | Type | Description |
|------|------|-------------|
| q | string | Full-text search (name, winery, region, sub-region, appellation, label text) |
| vintage | integer | Vintage year |
| grape | string | Grape variety (partial match) |
| winery | string | Winery name (partial match) |
| region | string | Region (partial match) |
| country | string | Country (partial match) |
| checked_in_after | datetime | Check-in date filter |
| checked_in_before | datetime | Check-in date filter |
| checked_out_after | datetime | Check-out date filter |
| checked_out_before | datetime | Check-out date filter |
| in_stock | boolean | Only in-stock wines |
| skip | integer | Pagination offset |
| limit | integer | Pagination limit |

**Response**: `200 OK`

Returns list of matching wines.

---

## Images

### GET /api/images/{filename}

Serve stored label images.

**Response**: Image file with appropriate content type.

---

## Export Endpoints

### GET /api/export/wines

Export wine collection in various formats.

**Query Parameters**:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| format | string | No | Export format: `csv`, `xlsx`, `json`, `yaml` (default: `json`) |
| in_stock | boolean | No | Only export in-stock wines |
| country | string | No | Filter by country |
| include_blends | boolean | No | Include grape blend summaries (default: true) |
| include_scores | boolean | No | Include score summaries (default: true) |

**CSV/XLSX columns**: `id`, `name`, `winery`, `vintage`, `grape_variety`, `region`, `country`, `alcohol_percentage`, `wine_type_id`, `price_tier`, `quantity`, `inventory_updated_at`, `grape_blend_summary`, `scores_summary`, `average_score`, `created_at`, `updated_at`, plus any custom field columns.

Custom fields are expanded into individual columns (sorted alphabetically) rather than a single JSON blob. For example, if wines have custom fields "Purchase Price" and "Cellar Location", those appear as separate columns in the export.

**Response**: File download (CSV/XLSX/YAML) or JSON body.

---

### GET /api/export/transactions

Export transaction history in various formats.

**Query Parameters**:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| format | string | No | Export format: `csv`, `xlsx`, `json`, `yaml` (default: `json`) |
| transaction_type | string | No | Filter: `CHECK_IN` or `CHECK_OUT` |
| wine_id | string | No | Filter by wine UUID |
| include_wine_details | boolean | No | Include wine name/vintage/winery (default: true) |

**Response**: File download or JSON body.

---

## Import Endpoints

### POST /api/import/upload

Upload a CSV or XLSX spreadsheet for import.

**Content-Type**: `multipart/form-data`

**Parameters**:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| file | file | Yes | CSV or XLSX file (max 10 MB) |

**Response**: `200 OK`
```json
{
  "batch_id": "abc123",
  "filename": "wines.csv",
  "row_count": 100,
  "headers": ["Name", "Country", "Vintage", "Price"],
  "preview_rows": [...],
  "suggested_mapping": {
    "Name": "name",
    "Country": "country",
    "Vintage": "vintage",
    "Price": "skip"
  },
  "mapping_source": "ai"
}
```

---

### POST /api/import/{batch_id}/mapping

Set or update column mapping for an import batch.

**Request Body**:
```json
{
  "mapping": {
    "Name": "name",
    "Country": "country",
    "Vintage": "vintage",
    "Purchase Price": "custom:Purchase Price",
    "Notes Column": "skip"
  }
}
```

Valid mapping targets: `name`, `winery`, `vintage`, `grape_variety`, `region`, `sub_region`, `appellation`, `country`, `alcohol_percentage`, `wine_type_id`, `classification`, `price_tier`, `quantity`, `notes`, `custom:<field_name>`, or `skip`.

**Response**: `200 OK` (updated batch info)

---

### POST /api/import/{batch_id}/process

Process an import batch to create wine records.

**Request Body** (optional):
```json
{
  "skip_non_wine": true,
  "default_quantity": 1
}
```

**Response**: `200 OK`
```json
{
  "batch_id": "abc123",
  "wines_created": 95,
  "rows_skipped": 5,
  "errors": [],
  "status": "completed"
}
```

---

## X-Wines Dataset Endpoints

The X-Wines endpoints provide access to a reference database of 100K+ wines with community ratings from the [X-Wines dataset](https://github.com/rogerioxavier/X-Wines).

### GET /api/xwines/search

Search X-Wines dataset for wine autocomplete.

**Query Parameters**:

| Name | Type | Required | Description |
|------|------|----------|-------------|
| q | string | Yes | Search query (min 2 characters) |
| limit | integer | No | Maximum results (default: 10, max: 50) |
| wine_type | string | No | Filter by wine type (Red, White, etc.) |
| country | string | No | Filter by country code (FR, US, etc.) |

**Response**: `200 OK`
```json
{
  "results": [
    {
      "id": 100062,
      "name": "Origem Merlot",
      "winery": "Casa Valduga",
      "wine_type": "Red",
      "country": "Brazil",
      "region": "Vale dos Vinhedos",
      "abv": 13.0,
      "avg_rating": 4.12,
      "rating_count": 21
    }
  ],
  "total": 2
}
```

---

### GET /api/xwines/wines/{wine_id}

Get full details for a specific X-Wines wine.

**Path Parameters**:
- `wine_id`: Integer ID of the wine

**Response**: `200 OK`
```json
{
  "id": 100062,
  "name": "Origem Merlot",
  "wine_type": "Red",
  "elaborate": "Varietal/100%",
  "grapes": "['Merlot']",
  "harmonize": "['Beef', 'Lamb', 'Veal']",
  "abv": 13.0,
  "body": "Full-bodied",
  "acidity": "Medium",
  "country_code": "BR",
  "country": "Brazil",
  "region_id": 1002,
  "region_name": "Vale dos Vinhedos",
  "winery_id": 10014,
  "winery_name": "Casa Valduga",
  "website": "http://www.casavalduga.com.br",
  "vintages": "[2020, 2019, 2018, 2017]",
  "avg_rating": 4.12,
  "rating_count": 21
}
```

---

### GET /api/xwines/stats

Get X-Wines dataset statistics.

**Response**: `200 OK`
```json
{
  "wine_count": 100646,
  "rating_count": 21013536,
  "version": "full",
  "import_date": "2024-01-15T10:30:00",
  "source": "https://github.com/rogerioxavier/X-Wines"
}
```

---

### GET /api/xwines/types

List distinct wine types in the dataset.

**Response**: `200 OK`
```json
["Dessert/Port", "Fortified", "Red", "Rosé", "Sparkling", "White"]
```

---

### GET /api/xwines/countries

List countries with wine counts.

**Response**: `200 OK`
```json
[
  {"code": "FR", "name": "France", "count": 25432},
  {"code": "IT", "name": "Italy", "count": 18234},
  {"code": "US", "name": "United States", "count": 15678}
]
```

---

## Error Responses

All endpoints may return these error responses:

### 400 Bad Request
```json
{
  "detail": "Description of the error"
}
```

### 404 Not Found
```json
{
  "detail": "Resource not found"
}
```

### 422 Unprocessable Entity
```json
{
  "detail": [
    {
      "loc": ["body", "field_name"],
      "msg": "Validation error message",
      "type": "error_type"
    }
  ]
}
```

### 500 Internal Server Error
```json
{
  "detail": "Internal server error"
}
```
