# Feature: User Preferences API

## Problem Statement

Users need a way to persist and retrieve their application preferences (theme, language, notification settings) so that their experience is consistent across sessions and devices. Currently, the `preferences-api` service contains only scaffold code with no preference management functionality.

## User Stories

- As a user, I want to save my theme preference (light/dark) so that the UI renders consistently across sessions.
- As a user, I want to save my language preference so that the application displays content in my chosen language.
- As a user, I want to configure my notification settings so that I only receive the notifications I care about.
- As a user, I want to retrieve all my preferences in a single request so that the frontend can initialize quickly.
- As a user, I want to update individual preferences without overwriting others so that partial updates are safe.

## Acceptance Criteria

- [ ] `GET /api/preferences-api/preferences/{user_id}` returns all preferences for the given user as key-value pairs
- [ ] `GET /api/preferences-api/preferences/{user_id}` returns an empty preferences object (not 404) for users with no saved preferences
- [ ] `PUT /api/preferences-api/preferences/{user_id}` creates or updates preferences for the given user (upsert semantics)
- [ ] `PUT /api/preferences-api/preferences/{user_id}` supports partial updates — only provided keys are changed, omitted keys are preserved
- [ ] Supported preference keys: `theme` (values: `light`, `dark`), `language` (ISO 639-1 codes, e.g. `en`, `es`, `fr`), `notifications_enabled` (boolean)
- [ ] Invalid preference values are rejected with a 400 Bad Request and descriptive error message
- [ ] Unknown preference keys are rejected with a 400 Bad Request
- [ ] Both endpoints require authentication — unauthenticated requests receive 401
- [ ] Users can only access their own preferences — mismatched user ID returns 403
- [ ] Preferences are persisted to PostgreSQL (not in-memory)
- [ ] All endpoints return responses in the standard `{data, meta}` envelope
- [ ] Endpoints are documented in OpenAPI spec via `spec.go`
- [ ] Domain layer validates preference keys and values independently of HTTP layer
- [ ] Service, handler, and repository layers follow existing hexagonal architecture patterns
- [ ] Unit tests cover service layer business logic (valid/invalid keys, partial updates, authorization)
- [ ] Integration tests cover handler layer HTTP behavior (status codes, response format, error cases)

## API Design

### GET /api/preferences-api/preferences/{user_id}

**Response 200:**
```json
{
  "data": {
    "user_id": "uuid",
    "preferences": {
      "theme": "dark",
      "language": "en",
      "notifications_enabled": true
    },
    "updated_at": "2026-02-08T12:00:00Z"
  },
  "meta": {
    "request_id": "...",
    "timestamp": "..."
  }
}
```

**Response 200 (no preferences saved yet):**
```json
{
  "data": {
    "user_id": "uuid",
    "preferences": {},
    "updated_at": null
  },
  "meta": { ... }
}
```

### PUT /api/preferences-api/preferences/{user_id}

**Request:**
```json
{
  "preferences": {
    "theme": "dark",
    "notifications_enabled": false
  }
}
```

**Response 200:**
```json
{
  "data": {
    "user_id": "uuid",
    "preferences": {
      "theme": "dark",
      "language": "en",
      "notifications_enabled": false
    },
    "updated_at": "2026-02-08T12:00:05Z"
  },
  "meta": { ... }
}
```

## Technical Constraints

- Must use PostgreSQL for persistence (migration in `services/preferences-api/migrations/`)
- Must follow the existing hexagonal architecture: domain → service → port → adapter
- Must use `app.Wrap()` handler pattern, `app.BindAndValidate()` for request binding
- Must use `httperror.*` for error responses, `httpresponse.*` for success responses
- Must use chi router with `{param}` brace syntax for URL parameters
- Must use `auth.Middleware()` for authentication and validate user ownership
- Preference keys are a closed set — only `theme`, `language`, `notifications_enabled` are accepted
- Preference values must be validated per-key (enum for theme, ISO code pattern for language, boolean for notifications)

## Database Schema

```sql
CREATE TABLE user_preferences (
    user_id     UUID PRIMARY KEY,
    preferences JSONB NOT NULL DEFAULT '{}',
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```

Storing preferences as JSONB allows flexible key-value storage while the application layer enforces the allowed key set and value validation.

## Dependencies

- PostgreSQL database accessible from the service (connection config via `DatabaseConfig`)
- Authentication system functional (`auth.Middleware()` with JWT validation)
- Existing `pkg/app`, `pkg/httperror`, `pkg/httpresponse`, `pkg/auth`, `pkg/openapi` packages

## Out of Scope

- Preference defaults / fallback values at the API level (frontend handles defaults)
- Preference history or versioning
- Bulk preference operations across multiple users
- Admin endpoints to manage other users' preferences
- Real-time preference change notifications (WebSocket/SSE)
- Custom/user-defined preference keys beyond the initial set

## Open Questions

1. **Language validation strictness:** Should language values be validated against a specific list of supported languages, or accept any valid ISO 639-1 code?
2. **Default preferences:** Should the API return default values for unset preferences (e.g., `theme: "light"` if never set), or let the frontend handle defaults?
3. **Rate limiting:** Should preference updates be rate-limited to prevent abuse?
4. **Removing the example scaffold:** Should the existing Example entity/handlers/routes be removed as part of this feature, or left for reference?
