# Community Gas Stations Feature

## Overview

The Community Gas Stations feature allows MotoVaultPro users to submit and discover community-verified 93 octane gas stations. Users can submit new station locations with details about fuel availability and pricing, and admins review submissions before they become publicly visible.

## Architecture

- **API Layer**: `api/community-stations.controller.ts`, `api/community-stations.routes.ts`, `api/community-stations.validation.ts`
- **Business Logic**: `domain/community-stations.service.ts`
- **Data Access**: `data/community-stations.repository.ts`
- **Database**: `community_stations` table
- **Types**: `domain/community-stations.types.ts`

## Database Schema

```sql
CREATE TABLE community_stations (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  submitted_by VARCHAR(255) NOT NULL,
  name VARCHAR(200) NOT NULL,
  address TEXT NOT NULL,
  city VARCHAR(100),
  state VARCHAR(50),
  zip_code VARCHAR(20),
  latitude DECIMAL(10, 8) NOT NULL,
  longitude DECIMAL(11, 8) NOT NULL,
  brand VARCHAR(100),
  has_93_octane BOOLEAN DEFAULT true,
  has_93_octane_ethanol_free BOOLEAN DEFAULT false,
  price_93 DECIMAL(5, 3),
  notes TEXT,
  status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'rejected')),
  reviewed_by VARCHAR(255),
  reviewed_at TIMESTAMP WITH TIME ZONE,
  rejection_reason TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_community_stations_status ON community_stations(status);
CREATE INDEX idx_community_stations_location ON community_stations(latitude, longitude);
CREATE INDEX idx_community_stations_submitted_by ON community_stations(submitted_by);
CREATE INDEX idx_community_stations_created_at ON community_stations(created_at DESC);
```

## API Endpoints

### User Endpoints (Authentication Required)

#### Submit a New Station
```
POST /api/stations/community
Authorization: Bearer <JWT>
Content-Type: application/json

{
  "name": "Shell Gas Station",
  "address": "123 Main St",
  "city": "Springfield",
  "state": "IL",
  "zipCode": "62701",
  "latitude": 39.7817,
  "longitude": -89.6501,
  "brand": "Shell",
  "has93Octane": true,
  "has93OctaneEthanolFree": false,
  "price93": 3.50,
  "notes": "Excellent customer service"
}

Response: 201 Created
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "submittedBy": "auth0|user123",
  "name": "Shell Gas Station",
  "address": "123 Main St",
  "city": "Springfield",
  "state": "IL",
  "zipCode": "62701",
  "latitude": 39.7817,
  "longitude": -89.6501,
  "brand": "Shell",
  "has93Octane": true,
  "has93OctaneEthanolFree": false,
  "price93": 3.50,
  "notes": "Excellent customer service",
  "status": "pending",
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-01-15T10:30:00Z"
}
```

#### Get User's Submissions
```
GET /api/stations/community/mine?limit=100&offset=0
Authorization: Bearer <JWT>

Response: 200 OK
{
  "total": 3,
  "stations": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "submittedBy": "auth0|user123",
      "name": "Shell Gas Station",
      "address": "123 Main St",
      "status": "pending",
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ]
}
```

#### Withdraw a Pending Submission
```
DELETE /api/stations/community/:id
Authorization: Bearer <JWT>

Response: 204 No Content
```

Conditions:
- User can only withdraw their own submissions
- Can only withdraw submissions in 'pending' status
- Approved or rejected submissions cannot be withdrawn

#### Get Approved Stations
```
GET /api/stations/community/approved?limit=100&offset=0
Authorization: Bearer <JWT>

Response: 200 OK
{
  "total": 42,
  "stations": [
    {
      "id": "223e4567-e89b-12d3-a456-426614174000",
      "submittedBy": "auth0|user456",
      "name": "Chevron Station",
      "address": "456 Oak Ave",
      "city": "Springfield",
      "state": "IL",
      "latitude": 39.7850,
      "longitude": -89.6480,
      "brand": "Chevron",
      "has93Octane": true,
      "has93OctaneEthanolFree": true,
      "price93": 3.45,
      "status": "approved",
      "reviewedBy": "auth0|admin123",
      "reviewedAt": "2024-01-14T15:20:00Z",
      "createdAt": "2024-01-10T08:00:00Z",
      "updatedAt": "2024-01-14T15:20:00Z"
    }
  ]
}
```

#### Find Nearby Approved Stations
```
POST /api/stations/community/nearby
Authorization: Bearer <JWT>
Content-Type: application/json

{
  "latitude": 39.7817,
  "longitude": -89.6501,
  "radiusKm": 50
}

Response: 200 OK
{
  "stations": [
    {
      "id": "223e4567-e89b-12d3-a456-426614174000",
      "submittedBy": "auth0|user456",
      "name": "Chevron Station",
      "address": "456 Oak Ave",
      "latitude": 39.7850,
      "longitude": 39.7850,
      "has93Octane": true,
      "status": "approved"
    }
  ]
}
```

Parameters:
- `latitude`: Required. Must be between -90 and 90
- `longitude`: Required. Must be between -180 and 180
- `radiusKm`: Optional. Defaults to 50 km. Must be between 1 and 500 km

### Admin Endpoints (Admin Role Required)

#### List All Submissions
```
GET /api/admin/community-stations?status=pending&submittedBy=auth0|user123&limit=100&offset=0
Authorization: Bearer <JWT>
X-Admin-Required: true

Response: 200 OK
{
  "total": 15,
  "stations": [...]
}
```

Filter parameters:
- `status`: Optional. Values: 'pending', 'approved', 'rejected'
- `submittedBy`: Optional. User ID to filter by submitter
- `limit`: Optional. Default 100, max 1000
- `offset`: Optional. Default 0

#### Get Pending Review Queue
```
GET /api/admin/community-stations/pending?limit=100&offset=0
Authorization: Bearer <JWT>
X-Admin-Required: true

Response: 200 OK
{
  "total": 8,
  "stations": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "submittedBy": "auth0|user123",
      "name": "Shell Gas Station",
      "address": "123 Main St",
      "city": "Springfield",
      "state": "IL",
      "latitude": 39.7817,
      "longitude": -89.6501,
      "brand": "Shell",
      "has93Octane": true,
      "has93OctaneEthanolFree": false,
      "price93": 3.50,
      "notes": "Excellent customer service",
      "status": "pending",
      "createdAt": "2024-01-15T10:30:00Z",
      "updatedAt": "2024-01-15T10:30:00Z"
    }
  ]
}
```

#### Approve or Reject a Submission
```
PATCH /api/admin/community-stations/:id/review
Authorization: Bearer <JWT>
X-Admin-Required: true
Content-Type: application/json

{
  "status": "approved"
}

OR (for rejection)

{
  "status": "rejected",
  "rejectionReason": "Location data appears to be in another city. Please resubmit with correct address."
}

Response: 200 OK
{
  "id": "123e4567-e89b-12d3-a456-426614174000",
  "submittedBy": "auth0|user123",
  "name": "Shell Gas Station",
  "status": "approved",
  "reviewedBy": "auth0|admin123",
  "reviewedAt": "2024-01-16T09:15:00Z",
  "updatedAt": "2024-01-16T09:15:00Z"
}
```

Requirements:
- For approval: Just set `status` to "approved"
- For rejection: Must provide `rejectionReason` explaining why the submission was rejected
- All review actions are logged in audit logs

## Validation Rules

### Station Submission
- `name`: Required, 1-200 characters
- `address`: Required
- `city`: Optional, max 100 characters
- `state`: Optional, max 50 characters
- `zipCode`: Optional, max 20 characters
- `latitude`: Required, must be between -90 and 90
- `longitude`: Required, must be between -180 and 180
- `brand`: Optional, max 100 characters
- `has93Octane`: Optional boolean, defaults to true
- `has93OctaneEthanolFree`: Optional boolean, defaults to false
- `price93`: Optional positive number
- `notes`: Optional string

### Review
- `status`: Required. Must be either 'approved' or 'rejected'
- `rejectionReason`: Required if status is 'rejected', optional if 'approved'

## Caching Strategy

The service implements Redis caching with 5-minute TTL for:
- Approved stations list (paginated)
- Nearby approved stations searches
- Pending submissions count

Cache is invalidated on:
- New submission
- Station review (approval or rejection)

Cache keys:
- `mvp:community-stations:approved:{limit}:{offset}`
- `mvp:community-stations:nearby:{latitude}:{longitude}:{radius}`
- `mvp:community-stations:pending:*` (pattern for invalidation)

## Error Codes

| Status | Code | Message |
|--------|------|---------|
| 400 | Validation error | Invalid request parameters |
| 400 | Bad Request | Cannot withdraw non-pending submission |
| 400 | Bad Request | Rejection reason required when rejecting |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Admin role required |
| 404 | Not Found | Station not found |
| 404 | Not Found | Submission not found |
| 500 | Internal Server Error | Unexpected error |

## Audit Logging

All admin review actions are logged with:
- Admin ID (reviewer)
- Action type: 'REVIEW'
- Resource type: 'community_station'
- Context: { status, submittedBy, name }
- Timestamp

Admin can access audit logs via: `GET /api/admin/audit-logs`

## Features Implemented

- [x] User submission of gas station locations
- [x] Validation of coordinates and required fields
- [x] User withdrawal of pending submissions
- [x] Public listing of approved stations
- [x] Location-based search for nearby approved stations
- [x] Admin approval/rejection workflow
- [x] Rejection reason documentation
- [x] Redis caching for performance
- [x] Audit logging for admin actions
- [x] User ownership validation
- [x] Comprehensive error handling
- [x] Pagination support
- [x] TypeScript type safety with Zod validation

## Testing

### Unit Tests
- Service business logic
- Cache invalidation
- Validation rules
- User ownership checks

Location: `tests/unit/community-stations.service.test.ts`

### Integration Tests
- Full API workflows
- Database interactions
- Authentication and authorization
- Error handling

Location: `tests/integration/community-stations.api.test.ts`

## Development Notes

### Running Tests
```bash
npm test -- features/stations/tests/unit/community-stations.service.test.ts
npm test -- features/stations/tests/integration/community-stations.api.test.ts
```

### Database Migration
Migration file: `migrations/004_create_community_stations.sql`

Apply with:
```bash
make migrate
```

### Feature Capsule Pattern
This feature follows the modular monolith pattern:
- Self-contained within `backend/src/features/stations/`
- No cross-feature imports (except through shared-minimal)
- Complete API layer with validation
- Business logic in service layer
- Data access isolated in repository
- Comprehensive test coverage

## Future Enhancements

Potential improvements:
- Image uploads for gas station photos
- User ratings/reviews of gas stations
- Integration with fuel price APIs
- Duplicate detection for same location
- Bulk import from external APIs
- Advanced search (filtering by brand, features)
- User moderation/reputation system
