# Stats API

## Overview

The `/api/stats/*` pattern provides dealer-facing metrics and analytics. Each category of stats gets its own endpoint under this namespace.

## Endpoints

### GET /api/stats/cms

Returns CMS statistics for the authenticated dealer.

**Authentication:** Required (session-based)
**Tier:** Professional only

**Response:**

```json
{
  "cms": {
    "pages": 5,
    "blogs": 3,
    "total": 8,
    "lastPublished": "2025-12-01T14:30:00.000Z"
  }
}
```

| Field           | Type           | Description                         |
| --------------- | -------------- | ----------------------------------- |
| `pages`         | number         | Count of pages (type = 'page')      |
| `blogs`         | number         | Count of blog posts (type = 'blog') |
| `total`         | number         | Combined count                      |
| `lastPublished` | string \| null | ISO date of most recent publish     |

**Performance:** Uses the `@@index([dealerId, type, status])` composite index on the Page model. Queries are dealer-scoped and return instantly even at scale.

## Usage

The CMS dashboard (`/dashboard/cms`) fetches these stats to display the Quick Stats card.

```typescript
const response = await fetch('/api/stats/cms');
const { cms } = await response.json();
```

## Admin Analytics

These admin-only endpoints power the dealer detail modal and the Platform Analytics tool on `/admin`. All require an admin session (or an active admin impersonation session) via `requireAdmin`; none are tier-gated.

### GET /api/admin/performance

Platform-wide aggregate traffic, inquiries, and link clicks across **all** dealers, with a comparison to the immediately-preceding window.

**Query params:**

| Param    | Type   | Description                                                          |
| -------- | ------ | ------------------------------------------------------------------- |
| `period` | string | `7d` \| `30d` \| `90d` \| `ytd` \| `custom` (default `30d`)          |
| `start`  | string | ISO date - required when `period=custom`                            |
| `end`    | string | ISO date - required when `period=custom`                            |

A `custom` period with a missing or unparseable `start`/`end` returns **400 `invalid_date_range`**. Each metric reports `current`, `previous`, and `changePercent` (rounded to one decimal). Responses are cached per-admin for 5 minutes (`Cache-Control: private, max-age=300`, `Vary: Cookie`); distinct period/range query strings cache independently.

**Performance:** Aggregates filter by `timestamp` only (no `dealerId`), so they rely on the standalone `@@index([timestamp])` on `PageView`, `LinkClick`, and `Lead`.

### GET /api/admin/dealers/[id]/stats

Last-30-days traffic, inquiries, and total link clicks for a single dealer. Used by the Dealer Detail modal's "Site Performance" row.

### `adminBypass` on GET /api/stats/dashboard

When an admin or superadmin in an active impersonation session views a dealer whose tier does not include analytics, `/api/stats/dashboard` returns **200** (instead of the usual `upgrade_required` 403) with an extra field:

```json
{ "adminBypass": { "dealerTier": "starter" } }
```

The dashboard UI uses this to render metrics grayed-out (lock icon, disabled click cards) with an admin-only banner, rather than the upgrade wall. The route also sets `Cache-Control: private, no-cache` + `Vary: Cookie` so a cached pre-impersonation payload is never served after the session changes.

## Future Expansion

Additional metrics should follow the `/api/stats/*` pattern:

| Endpoint             | Purpose                               |
| -------------------- | ------------------------------------- |
| `/api/stats/traffic` | Page views, unique visitors           |
| `/api/stats/revenue` | Stripe subscription/payment summaries |
| `/api/stats/leads`   | Contact form submissions, conversions |
| `/api/stats/summary` | Combined dashboard overview           |

When adding new stats endpoints:

1. Create route at `app/api/stats/{category}/route.ts`
2. Follow the same auth pattern (session + dealer lookup)
3. Use existing database indexes where possible
4. Return data nested under the category key (e.g., `{ traffic: {...} }`)
