# Progress Routes Analysis & Thoughts

## Overview

This document explores the current state of progress tracking in Bookmann, the migration from legacy media-item-specific routes to universal cross-device progress, and considerations for the future.

---

## Current State

### Legacy Routes (Marked as Deprecated)

Located in `internal/handlers/ebook.go:114-117`:

```go
// Legacy progress routes (deprecated - use universal progress instead)
g.GET("/api/media-items/:id/progress", h.GetMediaReadingProgress)
g.PUT("/api/media-items/:id/progress", h.UpdateMediaReadingProgress)
g.DELETE("/api/media-items/:id/progress", h.DeleteMediaReadingProgress)
```

**Purpose**: These routes handle progress tracking for a specific media item from the `media_items` table.

**Data Source**: Likely queries the `reading_progress` table filtered by `media_item_id`.

**Current Status**: Explicitly marked as "legacy" and "deprecated" in code comments.

---

### Universal Progress Routes (Phase 1 Implementation)

Located in `internal/handlers/ebook.go:119-122`:

```go
// Universal Progress routes (Phase 1)
g.GET("/api/progress/:id", h.GetUniversalProgress)
g.POST("/api/progress/:id", h.UpdateUniversalProgress)
g.GET("/api/progress/:id/history", h.GetProgressHistory)
```

**Purpose**: These routes provide "universal" progress tracking that works across devices and media types.

**Data Source**: Uses enhanced `reading_progress` table with additional fields:
- `percentage` - Universal percentage (0-1)
- `character_offset` - Character-based positioning
- `epubcfi` - EPUB Canonical Fragment Identifier
- `chapter` + `chapter_progress` - Chapter-based tracking
- Viewport coordinates (viewport_x, viewport_y, zoom_level)
- Scroll positions (scroll_position_x, scroll_position_y)
- Panel number for comics/manga
- Reading mode indicator

**Device Sync Metadata**:
- `last_sync_device` - Which device last updated
- `last_sync_source` - Source type (koreader, kobo, web, etc.)
- `last_sync_timestamp` - When sync occurred
- `conflict_detected` - Boolean flag for conflicts
- `conflict_resolved` - Boolean flag for resolution status

---

## Why the Migration Happened

### 1. **Cross-Platform Kindle Ecosystem Vision**

Bookmann aims to replace the Kindle ecosystem, which requires:
- Syncing progress across multiple devices (Kindle, Kobo, phone, web)
- Handling different progress formats (page numbers, percentages, CFI, character offsets)
- Maintaining reading state across different device types
- Supporting offline reading with sync queues

### 2. **Format Diversity**

Different e-readers and formats use different progress indicators:

| Format/Device | Progress Type | Example |
|---------------|---------------|---------|
| EPUB (KOReader) | EPUBCFI | `epubcfi(/6/4[chap1ref]!/4/2/1:0)` |
| EPUB (Kobo) | Page # + Total | `page 234 of 456` |
| PDF | Page # | `page 45` |
| Web Reader | Percentage | `0.45 (45%)` |
| TXT/Mobi | Character Offset | `offset 12345` |
| Comics/Manga | Panel # | `panel 7` |
| Kindle | Location # | `location 1234` |

The legacy `media-items/:id/progress` routes couldn't handle this diversity.

### 3. **Device Sync Architecture**

Universal progress enables:
- Real-time sync via WebSocket (`/ws/sync`)
- Offline queue support (`/api/queue/*`)
- Conflict detection and resolution
- Checkpoint mode for battery optimization
- Progress history tracking

---

## Current Database Schema

From `database/schema/schema.sql:130-158`:

```sql
CREATE TABLE reading_progress (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,

    -- Legacy fields
    current_page INTEGER DEFAULT 0,
    total_pages INTEGER,
    last_read_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),

    -- Universal Progress Tracking (Phase 1)
    percentage FLOAT CHECK (percentage >= 0 AND percentage <= 1),
    character_offset BIGINT,
    epubcfi TEXT,
    chapter INTEGER,
    chapter_progress FLOAT CHECK (chapter_progress >= 0 AND chapter_progress <= 1),
    viewport_x FLOAT DEFAULT 0,
    viewport_y FLOAT DEFAULT 0,
    zoom_level FLOAT DEFAULT 1.0,
    scroll_position_x FLOAT DEFAULT 0,
    scroll_position_y FLOAT DEFAULT 0,
    panel_number INTEGER,
    reading_mode VARCHAR(20),

    -- Device Sync Metadata (Phase 1)
    last_sync_device VARCHAR(50),
    last_sync_source VARCHAR(20),
    last_sync_timestamp TIMESTAMP WITH TIME ZONE,
    conflict_detected BOOLEAN DEFAULT FALSE,
    conflict_resolved BOOLEAN DEFAULT TRUE,

    UNIQUE(media_item_id, user_id)
);
```

**Backward Compatibility View** (Line 160-168):

```sql
CREATE VIEW ebook_reading_progress AS
SELECT rp.*,
       mi.id as ebook_id -- Map media_item_id to ebook_id for compatibility
FROM reading_progress rp
JOIN media_items mi ON rp.media_item_id = mi.id
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
WHERE lt.name = 'ebooks';
```

---

## The Migration Challenge

### Issue: Two Parallel Systems

Currently, **both** systems exist side-by-side:

1. **Legacy routes** (`/api/media-items/:id/progress`)
   - Likely use simple `current_page` / `total_pages` fields
   - Media-item scoped
   - No device sync metadata

2. **Universal routes** (`/api/progress/:id`)
   - Use rich progress tracking with multiple formats
   - Device-aware
   - Include sync metadata

### Question: What Does `:id` Mean?

**Legacy**: `:id` = `media_item_id` (UUID of the book)

**Universal**: `:id` = ??? (Could be same media_item_id, or could be a different identifier)

**Ambiguity**: The routes use the same parameter name but might mean different things.

### Potential Problems

1. **Data Duplication**: If both systems write to `reading_progress` table, they might overwrite each other
2. **Client Confusion**: Which endpoint should clients use?
3. **Migration Path**: How do existing clients using legacy endpoints transition?
4. **API Consistency**: Having two different endpoints for similar functionality is confusing

---

## Observations & Concerns

### 1. **Incomplete Migration**

The legacy routes are marked as deprecated but **still active**. This suggests:
- Migration is ongoing, not complete
- Some clients might still depend on legacy routes
- Fear of breaking existing integrations

### 2. **Backward Compatibility View**

The `ebook_reading_progress` view exists to maintain compatibility with the old `ebooks` table. This adds:
- Query overhead (JOINs to filter by library type)
- Developer confusion (which table/view to query?)
- Technical debt (maintaining two ways to access data)

### 3. **Route Naming Inconsistency**

- Legacy: `/api/media-items/:id/progress` (RESTful, nested under media-item)
- Universal: `/api/progress/:id` (flat structure, not nested)

**Question**: Should universal progress be under `/api/media-items/:id/universal-progress` for consistency?

### 4. **HTTP Method Mismatch**

Legacy routes use:
- `PUT /api/media-items/:id/progress` (update progress)

Universal routes use:
- `POST /api/progress/:id` (update progress)

**REST convention**: `PUT` is idempotent, `POST` is not. For progress updates, `PUT` might be more appropriate since setting the same progress twice should have the same effect.

### 5. **Missing Delete Operation**

Universal routes don't have a `DELETE /api/progress/:id` endpoint. Legacy does:
- `DELETE /api/media-items/:id/progress` (clear progress)

**Question**: Should there be a way to reset progress via universal routes?

---

## Potential Future Directions

### Option 1: Full Migration (Clean Break)

**Action**: Remove all legacy routes and views.

**Steps**:
1. Deprecate legacy routes in API documentation (return `Warning` header)
2. Add a 6-month migration timeline
3. Remove `/api/media-items/:id/progress` routes
4. Drop `ebook_reading_progress` view
5. Update all clients to use universal routes

**Pros**:
- Cleaner API surface
- Single source of truth
- Less maintenance burden
- Clearer documentation

**Cons**:
- Breaking change for existing clients
- Mobile apps might need updates
- External integrations could break

### Option 2: Compatibility Layer (Adapter Pattern)

**Action**: Keep legacy routes but make them thin wrappers around universal routes.

**Implementation**:
```go
// Legacy route calls universal route internally
func (h *Handler) GetMediaReadingProgress(c echo.Context) error {
    mediaItemID := c.Param("id")
    // Extract user_id from JWT
    // Call h.GetUniversalProgress with same IDs
    // Transform response if needed
}
```

**Pros**:
- No breaking changes
- Gradual migration path
- Single implementation (universal routes)

**Cons**:
- Maintains API surface area
- Slight performance overhead (function call)
- Still confusing to have two endpoints

### Option 3: Unified Endpoint (Best of Both)

**Action**: Create a single endpoint that handles both use cases.

**Proposed**:
```
GET /api/media-items/:id/progress?format=universal
PUT /api/media-items/:id/progress?format=universal
DELETE /api/media-items/:id/progress
```

The `format` query parameter determines:
- `format=simple` (default): Returns basic page/percentage (legacy behavior)
- `format=universal`: Returns full device-aware progress with metadata

**Pros**:
- Single endpoint
- Backward compatible
- Clear migration path via query parameter
- RESTful structure (nested under media-items)

**Cons**:
- More complex handler logic
- Need to maintain both formats in response

### Option 4: Versioned API (Cleanest Long-Term)

**Action**: Use API versioning to separate old and new.

**Proposed**:
```
# v1 (Legacy)
GET /api/v1/media-items/:id/progress
PUT /api/v1/media-items/:id/progress
DELETE /api/v1/media-items/:id/progress

# v2 (Universal)
GET /api/v2/media-items/:id/progress
PUT /api/v2/media-items/:id/progress
GET /api/v2/media-items/:id/progress/history
```

**Pros**:
- Clean separation
- Can deprecate v1 independently
- Standard industry practice
- Clear migration documentation

**Cons**:
- Need to implement version routing
- More upfront work
- Maintenance of two versions temporarily

---

## Unanswered Questions for Discussion

1. **Are any clients currently using the legacy progress routes?**
   - If yes, which ones? (mobile app, web app, third-party integrations?)
   - Can they be updated easily?

2. **What does the `:id` parameter represent in universal progress routes?**
   - Is it still `media_item_id`?
   - Or is it a `reading_progress` record ID?
   - Need to check implementation to confirm

3. **Why was `/api/progress/:id` chosen instead of `/api/media-items/:id/universal-progress`?**
   - Flat structure vs nested structure design decision
   - Might indicate plans for progress to exist independently of media items?

4. **Is the legacy route implementation actually different, or just deprecated?**
   - Need to read the handler implementations to compare
   - They might be calling the same underlying code

5. **Should we maintain progress deletion functionality?**
   - Universal routes don't have DELETE
   - Is deleting progress a necessary feature?

6. **What's the timeline for removing legacy routes?**
   - Already marked deprecated, but when can we delete them?
   - Need to coordinate with mobile app releases

7. **How does the backward compatibility view affect performance?**
   - The `ebook_reading_progress` view requires JOINs
   - Is it used anywhere, or can it be dropped?

---

## Recommendations

### Immediate Actions (Discussion Phase)

1. **Audit Current Usage**
   - Search codebase for references to legacy routes
   - Check if any external documentation mentions these endpoints
   - Identify all clients (web, mobile, third-party)

2. **Compare Implementations**
   - Read handler code for both legacy and universal routes
   - Document differences in behavior
   - Determine if they're truly different or just deprecated wrappers

3. **Clarify API Contract**
   - Define what `:id` means in universal routes
   - Document expected request/response formats
   - Add examples for different device types

4. **Performance Analysis**
   - Query database to see how many records use legacy fields vs universal
   - Check if backward compatibility view is actually used
   - Benchmark query performance with/without views

### Future Considerations

1. **Choose a Migration Strategy**
   - Review Options 1-4 above
   - Consider breaking changes vs compatibility
   - Plan timeline based on client usage

2. **API Versioning Decision**
   - Decide if we want `/api/v1/` and `/api/v2/` structure
   - Or use different approach (headers, content negotiation)

3. **Documentation Updates**
   - Update API_REFERENCE.md with clear deprecation notices
   - Add migration guide for clients
   - Document best practices for progress tracking

4. **Test Coverage**
   - Ensure both legacy and universal routes have comprehensive tests
   - Add integration tests for cross-device sync scenarios
   - Test conflict resolution workflows

---

## Next Steps for Discussion

1. **Review handler implementations** to understand actual differences
2. **Check client usage** (web app, mobile apps, Bruno tests)
3. **Decide on migration timeline** and breaking change tolerance
4. **Choose unified strategy** (Options 1-4 or hybrid)
5. **Plan implementation** with backward compatibility in mind

---

*Document created for future discussion. No changes to be made without review.*
