# Phase 3: Hooks Extraction

## Goal

Extract duplicated video position caching logic into a reusable hook.

## Current Duplication

The position caching pattern is duplicated in:
1. `VidstackProvider.tsx` (lines ~180-220)
2. `StreamProvider.tsx` (lines ~200-240)

Both implement:
- Save position every 5 seconds during playback
- Restore position when video loads (canPlay)
- Clear position when video ends

## File to Create

### hooks/useVideoPositionCache.ts

```typescript
'use client';

/**
 * useVideoPositionCache - Manages video playback position caching
 *
 * Saves position periodically during playback and restores on load.
 */

import { useRef, useEffect, useCallback } from 'react';
import { useVideoCache } from '../../../stores/mediaCache';

// =============================================================================
// TYPES
// =============================================================================

export interface UseVideoPositionCacheOptions {
  /** Unique key for caching (e.g., video URL or stream key) */
  cacheKey: string;
  /** Current playback time in seconds */
  currentTime: number;
  /** Video duration in seconds */
  duration: number;
  /** Whether video is currently playing */
  isPlaying: boolean;
  /** Whether video is ready to play */
  isReady: boolean;
  /** Callback to seek to a specific time */
  onSeek: (time: number) => void;
}

export interface UseVideoPositionCacheReturn {
  /** Manually save current position */
  savePosition: () => void;
  /** Clear saved position */
  clearPosition: () => void;
}

// =============================================================================
// CONSTANTS
// =============================================================================

/** Save interval in seconds */
const SAVE_INTERVAL = 5;

/** Minimum position to restore (avoid 0 or near-end) */
const MIN_RESTORE_POSITION = 1;

// =============================================================================
// HOOK
// =============================================================================

export function useVideoPositionCache(
  options: UseVideoPositionCacheOptions
): UseVideoPositionCacheReturn {
  const { cacheKey, currentTime, duration, isPlaying, isReady, onSeek } = options;

  const { saveVideoPosition, getVideoPosition } = useVideoCache();
  const lastSavedTimeRef = useRef<number>(0);
  const hasRestoredRef = useRef<boolean>(false);

  // Restore position when ready
  useEffect(() => {
    if (!isReady || !cacheKey || hasRestoredRef.current) return;

    const savedPosition = getVideoPosition(cacheKey);
    if (savedPosition && savedPosition > MIN_RESTORE_POSITION && savedPosition < duration - 1) {
      onSeek(savedPosition);
    }
    hasRestoredRef.current = true;
  }, [isReady, cacheKey, duration, getVideoPosition, onSeek]);

  // Reset restored flag when cache key changes
  useEffect(() => {
    hasRestoredRef.current = false;
    lastSavedTimeRef.current = 0;
  }, [cacheKey]);

  // Save position periodically during playback
  useEffect(() => {
    if (!cacheKey || !isPlaying || currentTime <= 0) return;

    const timeSinceLastSave = currentTime - lastSavedTimeRef.current;
    if (timeSinceLastSave >= SAVE_INTERVAL || timeSinceLastSave < 0) {
      saveVideoPosition(cacheKey, currentTime);
      lastSavedTimeRef.current = currentTime;
    }
  }, [cacheKey, isPlaying, currentTime, saveVideoPosition]);

  // Save position on pause
  useEffect(() => {
    if (!cacheKey || isPlaying || currentTime <= 0) return;

    saveVideoPosition(cacheKey, currentTime);
    lastSavedTimeRef.current = currentTime;
  }, [cacheKey, isPlaying, currentTime, saveVideoPosition]);

  const savePosition = useCallback(() => {
    if (cacheKey && currentTime > 0) {
      saveVideoPosition(cacheKey, currentTime);
    }
  }, [cacheKey, currentTime, saveVideoPosition]);

  const clearPosition = useCallback(() => {
    if (cacheKey) {
      saveVideoPosition(cacheKey, 0);
    }
  }, [cacheKey, saveVideoPosition]);

  return {
    savePosition,
    clearPosition,
  };
}
```

### hooks/index.ts

```typescript
/**
 * VideoPlayer hooks - Public API
 */

export { useVideoPositionCache } from './useVideoPositionCache';
export type {
  UseVideoPositionCacheOptions,
  UseVideoPositionCacheReturn,
} from './useVideoPositionCache';
```

## Usage in Providers

After extraction, providers can use:

```typescript
// In VidstackProvider.tsx
const { clearPosition } = useVideoPositionCache({
  cacheKey: sourceKey,
  currentTime: player?.currentTime ?? 0,
  duration: player?.duration ?? 0,
  isPlaying: !paused,
  isReady: canPlay,
  onSeek: (time) => player?.currentTime = time,
});

// Clear on video end
useEffect(() => {
  if (ended) clearPosition();
}, [ended, clearPosition]);
```

## Benefits

1. **DRY** - Single source of truth for position caching logic
2. **Testable** - Hook can be unit tested independently
3. **Reusable** - Can be used in future video components
4. **Maintainable** - Changes only need to be made in one place
