# Integration Point Refactor - Implementation Context

**Author**: Matthew Raymer  
**Date**: 2025-10-29  
**Status**: 🎯 **CONTEXT** - Pre-implementation analysis and mapping

> **See also:** [REFACTOR_ANALYSIS.md](./REFACTOR_ANALYSIS.md) for architectural analysis | [REFACTOR_NOTES_QUICK_START.md](./REFACTOR_NOTES_QUICK_START.md) for quick start

## Purpose

This document maps the current codebase to the Integration Point Refactor plan, identifies what exists, what needs to be created, and where gaps exist before starting implementation (PR1).

---

## Current State Analysis

### ✅ What Already Exists

#### 1. TypeScript Type Definitions (`src/types/content-fetcher.ts`)
- ✅ `NotificationContent` interface (matches directive spec)
- ✅ `FetchContext` interface (matches directive spec)
- ✅ `JsNotificationContentFetcher` interface (matches directive spec)
- ✅ `FetchTrigger` type (matches directive spec)
- ✅ `SchedulingPolicy` interface (matches directive spec)

**Status**: Complete and ready for PR1. No changes needed.

#### 2. Example Implementations
- ✅ `examples/native-fetcher-android.kt` - Kotlin native fetcher skeleton
- ✅ `examples/js-fetcher-typescript.ts` - TypeScript JS fetcher skeleton

**Status**: Examples exist as reference. These are for host app, not plugin.

#### 3. Test Contract (`tests/fixtures/test-contract.json`)
- ✅ Golden contract fixture with expected outputs
- ✅ Failure scenarios defined

**Status**: Ready for contract tests. Will be used in PR2+ for validation.

#### 4. Current TimeSafari Integration (To Be Replaced)

**Location**: `android/plugin/src/main/java/com/timesafari/dailynotification/`

- ✅ `TimeSafariIntegrationManager.java` - Current TimeSafari orchestration
- ✅ `EnhancedDailyNotificationFetcher.java` - TimeSafari API client
- ✅ `DailyNotificationJWTManager.java` - JWT generation for TimeSafari
- ✅ `DailyNotificationETagManager.java` - ETag caching for TimeSafari

**Status**: These will be moved to host app OR deprecated behind feature flag (PR7).

**Current Integration Flow**:
```
DailyNotificationPlugin.load()
  → TimeSafariIntegrationManager (created in plugin)
    → EnhancedDailyNotificationFetcher
      → JWTManager + ETagManager
        → TimeSafari API endpoints
```

**Target Flow** (after refactor):
```
DailyNotificationPlugin.load()
  → NativeNotificationContentFetcher (registered by host app)
    → Host app's TimeSafari integration
      → TimeSafari API endpoints
```

---

## What Needs to Be Created

### PR1: SPI Shell (First Implementation)

#### 1. Android Native SPI Interface
**Location**: `android/plugin/src/main/java/com/timesafari/dailynotification/`

**File to Create**: `NativeNotificationContentFetcher.java`

```java
package org.timesafari.dailynotification;

public interface NativeNotificationContentFetcher {
    java.util.concurrent.CompletableFuture<java.util.List<NotificationContent>> 
        fetchContent(FetchContext context);
}

public class FetchContext {
    public final String trigger; // "background_work" | "prefetch" | "manual" | "scheduled"
    public final Long scheduledTime; // Optional: epoch ms
    public final long fetchTime; // Required: epoch ms
    public final Map<String, Object> metadata; // Optional
    
    // Constructor, getters...
}
```

**Status**: ❌ Not created yet - PR1 task

#### 2. Fetcher Registry in Plugin
**Location**: `android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.java`

**What to Add**:
- Field: `private NativeNotificationContentFetcher nativeFetcher;`
- Method: `public void setNativeFetcher(NativeNotificationContentFetcher fetcher)`
- Storage: Store in static registry or application-level singleton

**Status**: ❌ Not implemented yet - PR1 task

#### 3. SchedulingPolicy Implementation (Android)
**Location**: `android/plugin/src/main/java/com/timesafari/dailynotification/`

**File to Create**: `SchedulingPolicy.java`

```java
public class SchedulingPolicy {
    public Long prefetchWindowMs;
    public RetryBackoff retryBackoff;
    public Integer maxBatchSize;
    public Long dedupeHorizonMs;
    public Integer cacheTtlSeconds;
    public Boolean exactAlarmsAllowed;
    
    public static class RetryBackoff {
        public long minMs;
        public long maxMs;
        public double factor;
        public int jitterPct;
    }
}
```

**Status**: ❌ Not created yet - PR1 task

**Where Used**: 
- `DailyNotificationFetchWorker` - backoff policy
- `DailyNotificationScheduler` - prefetch timing
- Deduplication logic (new, PR4)
- TTL enforcement (existing `DailyNotificationTTLEnforcer`)

#### 4. Plugin API Methods (TypeScript Bridge)
**Location**: `android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.java`

**Methods to Add**:
```java
@PluginMethod
public void setJsContentFetcher(PluginCall call) {
    // Store JS fetcher for foreground use
    // Will be called from TypeScript
}

@PluginMethod  
public void enableNativeFetcher(PluginCall call) {
    // Toggle native fetcher on/off
    // Default: true (native required for background)
}

@PluginMethod
public void setPolicy(PluginCall call) {
    // Update SchedulingPolicy
    // Parse JSON from call, create SchedulingPolicy instance
}
```

**Status**: ❌ Not implemented yet - PR1 task

#### 5. TypeScript Plugin Interface Extensions
**Location**: `src/definitions.ts`

**What to Add**:
```typescript
export interface DailyNotificationPlugin {
  // ... existing methods ...
  
  setJsContentFetcher(fetcher: JsNotificationContentFetcher): void;
  enableNativeFetcher(enable: boolean): Promise<void>;
  setPolicy(policy: SchedulingPolicy): Promise<void>;
}
```

**Status**: ❌ Partially exists in `src/types/content-fetcher.ts` but not wired to `DailyNotificationPlugin` interface in `src/definitions.ts` - PR1 task

---

### PR2: Background Workers

#### Current Worker: `DailyNotificationFetchWorker.java`

**Location**: `android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationFetchWorker.java`

**Current Behavior**:
- Uses `DailyNotificationFetcher` (generic, no TimeSafari)
- Called from `DailyNotificationFetcher.scheduleFetch()`
- Runs via WorkManager

**What Needs to Change**:

1. **Remove TimeSafari-Specific Coordination**:
   ```java
   // Remove these lines (79-92):
   boolean timesafariCoordination = inputData.getBoolean("timesafari_coordination", false);
   // ... coordination checks ...
   ```

2. **Add Native Fetcher Call**:
   ```java
   private NotificationContent fetchContentWithTimeout() {
       if (nativeFetcher == null) {
           Log.w(TAG, "Native fetcher not registered, falling back to cache");
           return getCachedContent();
       }
       
       FetchContext context = new FetchContext(
           "background_work",
           scheduledTime,
           System.currentTimeMillis(),
           metadata
       );
       
       try {
           List<NotificationContent> results = 
               nativeFetcher.fetchContent(context)
                   .get(30, TimeUnit.SECONDS);
           return results.isEmpty() ? null : results.get(0);
       } catch (Exception e) {
           // Handle timeout, errors
           return null;
       }
   }
   ```

3. **Add SchedulingPolicy Backoff**:
   - Use `SchedulingPolicy.retryBackoff` for retry delays
   - Replace hardcoded retry logic

**Status**: ❌ Not modified yet - PR2 task

---

### PR3: JS Fetcher Path

#### Plugin Side: Bridge JS Fetcher to Workers

**Location**: `android/plugin/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.java`

**What Needs to Change**:

1. **Store JS Fetcher Reference**:
   ```java
   private JsNotificationContentFetcher jsFetcher; // Wrapper object needed
   
   @PluginMethod
   public void setJsContentFetcher(PluginCall call) {
       // Extract callback from JS
       // Store for foreground use only
       // Log warning if used in background worker
   }
   ```

2. **Foreground Fetch Method**:
   ```java
   @PluginMethod
   public void refreshNow(PluginCall call) {
       if (jsFetcher != null) {
           // Call JS fetcher (foreground only)
           // Must bridge JS callback to Java
       } else if (nativeFetcher != null) {
           // Fall back to native fetcher
       }
   }
   ```

**Challenge**: JavaScript → Java callback bridge. Options:
- Option A: Use Capacitor's bridge to call JS function
- Option B: Use event system (plugin emits event, JS responds)
- Option C: Store fetcher config, not callback (requires HTTP endpoint)

**Status**: ❌ Not implemented yet - PR3 task

---

### PR4: Deduplication

#### New Component: Deduplication Store

**Location**: `android/plugin/src/main/java/com/timesafari/dailynotification/`

**File to Create**: `NotificationDeduplicationStore.java`

**Responsibilities**:
- Maintain bloom filter or LRU cache of recent `dedupeKey` values
- Check `dedupeHorizonMs` window
- Prevent duplicate notifications within horizon
- Use `dedupeKey` (fallback to `id`)

**Integration Points**:
- `DailyNotificationScheduler.scheduleNotification()` - check before scheduling
- `DailyNotificationFetchWorker.doWork()` - check after fetch

**Status**: ❌ Not created yet - PR4 task

---

### PR5: Failure Policy

#### Enhance Existing Components

**Location**: Multiple files

**1. Retry Logic** (`DailyNotificationFetchWorker.java`):
- Replace hardcoded retry with `SchedulingPolicy.retryBackoff`
- Implement exponential backoff with jitter
- Distinguish retryable vs unretryable errors

**2. Cache Fallback** (new or enhance `DailyNotificationStorage.java`):
- Check TTL using `SchedulingPolicy.cacheTtlSeconds` or item `ttlSeconds`
- Fallback to cached content if fetch fails and cache valid

**3. Error Classification**:
```java
public enum FetchErrorType {
    RETRYABLE,    // 5xx, network, timeout
    UNRETRYABLE,  // 4xx auth, schema error
    PARTIAL,      // Some items bad
    OVER_QUOTA    // 429 with Retry-After
}
```

**Status**: ❌ Partially implemented (basic retry exists), needs enhancement - PR5 task

---

## Component Dependency Map

### Current Dependencies

```
DailyNotificationPlugin
  ├── TimeSafariIntegrationManager (TO BE REMOVED in PR7)
  │   ├── EnhancedDailyNotificationFetcher (TO BE MOVED to host app)
  │   ├── DailyNotificationJWTManager (TO BE MOVED to host app)
  │   └── DailyNotificationETagManager (TO BE MOVED to host app)
  ├── DailyNotificationScheduler
  │   ├── DailyNotificationTTLEnforcer
  │   └── PendingIntentManager
  ├── DailyNotificationFetchWorker
  │   └── DailyNotificationFetcher (generic, keep)
  └── DailyNotificationStorage
```

### Target Dependencies (After Refactor)

```
DailyNotificationPlugin
  ├── NativeNotificationContentFetcher (REGISTERED by host app)
  ├── JsNotificationContentFetcher (REGISTERED by host app, foreground only)
  ├── SchedulingPolicy (CONFIGURED by host app)
  ├── DailyNotificationScheduler
  │   ├── DailyNotificationTTLEnforcer
  │   ├── PendingIntentManager
  │   └── NotificationDeduplicationStore (NEW in PR4)
  ├── DailyNotificationFetchWorker
  │   ├── Calls NativeNotificationContentFetcher (native path)
  │   └── Uses SchedulingPolicy (backoff, TTL)
  └── DailyNotificationStorage
```

---

## Integration Points to Identify

### 1. Worker Enqueue Points

**Where Workers Are Scheduled**:

1. **`DailyNotificationFetcher.scheduleFetch()`** (line ~77-116):
   - Enqueues `DailyNotificationFetchWorker`
   - Currently generic (no TimeSafari coupling)

2. **`TimeSafariIntegrationManager.fetchAndScheduleFromServer()`** (line ~258):
   - May enqueue workers with TimeSafari coordination flags
   - Needs to switch to native fetcher path

3. **`DailyNotificationPlugin.scheduleCoordinatedBackgroundJobs()`**:
   - Phase 3 coordination code
   - Adds TimeSafari-specific flags to WorkManager Data
   - Needs refactor to remove TimeSafari flags

**Action for PR2**: Modify worker enqueue to use native fetcher, remove TimeSafari coordination flags.

---

### 2. Notification Content Flow

**Current Flow**:
```
EnhancedDailyNotificationFetcher.fetch()
  → Returns TimeSafariNotificationBundle
    → TimeSafariIntegrationManager.convertBundleToNotificationContent()
      → Returns List<NotificationContent>
        → DailyNotificationScheduler.scheduleNotification()
```

**Target Flow**:
```
NativeNotificationContentFetcher.fetchContent(context)
  → Returns List<NotificationContent> (already generic)
    → DailyNotificationScheduler.scheduleNotification()
```

**Gap**: `convertBundleToNotificationContent()` logic must move to host app (PR7).

---

### 3. TTL Enforcement Points

**Current TTL Flow**:
1. **Hard-Fail at Arming**: `->DailyNotificationScheduler.scheduleNotification()`
   - Calls `ttlEnforcer.validateBeforeArming(content)`
   - ✅ Already generic (works with any `NotificationContent`)

2. **Soft-Check at Fire**: `DailyNotificationReceiver.onReceive()`
   - Checks freshness, may trigger refresh
   - ✅ Already generic

**Status**: ✅ TTL enforcement is already generic - no changes needed for SPI.

---

### 4. Metrics Collection Points

**Current Metrics**: 
- `src/observability.ts` - TypeScript observability
- `packages/polling-contracts/src/telemetry.ts` - Telemetry manager

**What to Add**:
- `fetch_duration_ms` - Time for native fetcher to complete
- `fetch_success` - Boolean success/failure
- `fetch_fail_class` - Error classification (retryable/unretryable/partial/over-quota)
- `items_fetched` - Number of items from fetcher
- `items_enqueued` - Number successfully scheduled
- `deduped_count` - Items filtered by deduplication (PR4)
- `cache_hits` - Cached content used when fetch fails (PR5)

**Action for PR2**: Add metrics calls in `DailyNotificationFetchWorker.doWork()`.

---

## Migration Path Mapping

### Feature Flag Implementation (PR7)

**Location**: `DailyNotificationPlugin.java`

**What to Add**:
```java
private static final boolean USE_LEGACY_INTEGRATION = false; // Default false in new minor

private void fetchContent() {
    if (USE_LEGACY_INTEGRATION && timeSafariIntegration != null) {
        // Legacy path
        timeSafariIntegration.fetchAndScheduleFromServer(false);
    } else if (nativeFetcher != null) {
        // New SPI path
        // Call native fetcher
    } else {
        Log.w(TAG, "No fetcher available");
    }
}
```

**Files to Guard**:
- `TimeSafariIntegrationManager.java` - Keep for one minor release
- `EnhancedDailyNotificationFetcher.java` - Keep for one minor release
- Legacy `@PluginMethod` wrappers - Deprecate, keep for compatibility

---

## File Creation Checklist

### PR1: SPI Shell
- [ ] `android/plugin/src/main/java/com/timesafari/dailynotification/NativeNotificationContentFetcher.java`
- [ ] `android/plugin/src/main/java/com/timesafari/dailynotification/FetchContext.java` (or inner class)
- [ ] `android/plugin/src/main/java/com/timesafari/dailynotification/SchedulingPolicy.java`
- [ ] Update `DailyNotificationPlugin.java`:
  - [ ] Add `setNativeFetcher()` method
  - [ ] Add `setJsContentFetcher()` method (stub, full in PR3)
  - [ ] Add `enableNativeFetcher()` method
  - [ ] Add `setPolicy()` method
- [ ] Update `src/definitions.ts`:
  - [ ] Wire `SchedulingPolicy` from `content-fetcher.ts`
  - [ ] Add new methods to `DailyNotificationPlugin` interface

### PR2: Background Workers
- [ ] Modify `DailyNotificationFetchWorker.java`:
  - [ ] Remove TimeSafari coordination checks
  - [ ] Add native fetcher call
  - [ ] Add SchedulingPolicy backoff
  - [ ] Add metrics recording

### PR3: JS Fetcher Path
- [ ] Modify `DailyNotificationPlugin.java`:
  - [ ] Implement `setJsContentFetcher()` (JS bridge)
  - [ ] Add foreground refresh method
  - [ ] Add event bus for JS communication
- [ ] Create TypeScript bridge utilities

### PR4: Deduplication
- [ ] `android/plugin/src/main/java/com/timesafari/dailynotification/NotificationDeduplicationStore.java`
- [ ] Integrate into `DailyNotificationScheduler`
- [ ] Add metrics for deduped count

### PR5: Failure Policy
- [ ] Create `FetchErrorType` enum
- [ ] Enhance retry logic with SchedulingPolicy
- [ ] Add cache fallback mechanism
- [ ] Add error classification helpers

### PR6: Docs & Samples
- [ ] `INTEGRATION_GUIDE.md` - When to use native vs JS fetcher
- [ ] `SCHEDULING_POLICY.md` - All configuration knobs
- [ ] Update examples with real registration code

### PR7: Feature Flag Legacy
- [ ] Add `USE_LEGACY_INTEGRATION` flag
- [ ] Guard all TimeSafari code paths
- [ ] Update migration guide
- [ ] Regression tests

---

## Open Questions to Resolve

### Before PR1
1. **Fetcher Registration Pattern**:
   - Should `setNativeFetcher()` be called from host app's `Application.onCreate()`?
   - Or should it be a static registry accessible from host app's native code?
   - **Recommendation**: Static registry in plugin, host app registers in `Application.onCreate()`

2. **FetchContext Metadata**:
   - What should plugin populate in `metadata` map?
   - Current activeDid? App state? Network state?
   - **Recommendation**: Start minimal (empty map), extend later

### Before PR2
3. **Background Worker Timeout**:
   - Current: 30 seconds for fetch
   - Should this be configurable in `SchedulingPolicy`?
   - **Recommendation**: Add `fetchTimeoutMs` to `SchedulingPolicy` in PR1

4. **Multiple NotificationContent Handling**:
   - If native fetcher returns multiple items, should all be scheduled?
   - Should `maxBatchSize` apply here?
   - **Recommendation**: Schedule all valid items, `maxBatchSize` limits fetcher input/API calls

### Before PR3
5. **JS Fetcher Bridge Mechanism**:
   - Which approach: Capacitor bridge callback, event system, or HTTP endpoint?
   - **Recommendation**: Start with event system (plugin emits `fetch_needed`, JS responds via `setJsContentFetcher` callback)

6. **Foreground-Only Enforcement**:
   - How to prevent JS fetcher from being called in background worker?
   - **Recommendation**: Runtime guard + lint rule (document in PR3)

---

## Testing Strategy

### Contract Tests (After PR2)
- Use `tests/fixtures/test-contract.json`
- Run native fetcher with fixture context
- Compare normalized output to expected output
- **Location**: Create `android/plugin/src/test/java/com/timesafari/dailynotification/NativeFetcherContractTest.java`

### Integration Tests (After PR3)
- Test JS fetcher in foreground
- Test native fetcher in background worker
- Verify both produce identical results for same input

### Regression Tests (Before PR7)
- Ensure legacy TimeSafari path still works behind feature flag
- Verify new SPI path works independently
- Test migration from legacy to SPI path

---

## Next Steps

1. **Review this context document** - Verify completeness
2. **Resolve open questions** - Make decisions before PR1
3. **Create PR1 branch** - Start with SPI shell implementation
4. **Reference examples** - Use `examples/native-fetcher-android.kt` as pattern
5. **Follow 7-PR plan** - Sequential implementation with tests after each PR

---

**Status**: Context building complete - ready for PR1 implementation  
**Last Updated**: 2025-10-29  
**Maintainer**: Plugin development team

