/**
 * SyncManager Service
 *
 * Story 3.3: Manages offline sync queue for resilient local-first operations.
 *
 * Architecture Compliance:
 * - Service layer for sync queue operations
 * - Returns plain data objects, not Dexie observables
 * - Follows Logic Sandwich: UI -> Store -> SyncManager -> Dexie
 *
 * MVP Behavior:
 * - All actions are local-only (no server sync yet)
 * - SyncQueue is infrastructure for future server persistence
 * - processQueue() executes actions locally (like save/delete)
 */

import { db, type SyncQueueItem } from '../lib/db';
import { DraftService } from '../lib/db/draft-service';

/**
 * Supported sync action types
 */
export type SyncAction = SyncQueueItem['action'];

/**
 * Payload for saveDraft action
 */
export interface SaveDraftPayload {
  draftData: Omit<{
    sessionId: string;
    title: string;
    content: string;
    tags: string[];
    createdAt: number;
    status: 'draft' | 'completed' | 'regenerated';
    completedAt?: number;
  }, 'id'>;
}

/**
 * Payload for deleteDraft action
 */
export interface DeleteDraftPayload {
  draftId: number;
}

/**
 * Payload for completeDraft action
 */
export interface CompleteDraftPayload {
  draftId: number;
}

/**
 * Sync queue item with id (from database)
 */
interface SyncQueueItemWithId extends SyncQueueItem {
  id: number;
}

/**
 * SyncManager Service
 *
 * Manages the offline sync queue for actions that need to be synchronized.
 * For MVP, this processes actions locally (no server sync).
 * Post-MVP, this will sync actions to a server when online.
 */
export class SyncManager {
  /**
   * Queue an action for sync
   * Called when offline or when an action fails
   *
   * @param action - The type of action to queue
   * @param payload - Action payload
   * @returns The ID of the queued item
   */
  static async queueAction(
    action: SyncAction,
    payload: SaveDraftPayload | DeleteDraftPayload | CompleteDraftPayload
  ): Promise<number> {
    return await db.syncQueue.add({
      action,
      payload,
      status: 'pending',
      createdAt: Date.now(),
      retries: 0,
    });
  }

  /**
   * Process all pending actions in the queue
   * Called on reconnection or manually
   * For MVP: Executes actions locally only
   */
  static async processQueue(): Promise<void> {
    const pendingItems = await db.syncQueue
      .where('status')
      .equals('pending')
      .sortBy('createdAt');

    for (const item of pendingItems as SyncQueueItemWithId[]) {
      await this.executeItem(item);
    }
  }

  /**
   * Execute a single queued item with retry logic
   * Implements exponential backoff for retries
   *
   * Note: Each call to processQueue() will attempt each item once.
   * Items that fail are marked for retry and will be tried again
   * on the next processQueue() call (e.g., on reconnection).
   *
   * @param item - The queue item to execute
   */
  private static async executeItem(item: SyncQueueItemWithId): Promise<void> {
    // Mark as processing
    await db.syncQueue.update(item.id, { status: 'processing' });

    try {
      // Execute the action
      // Cast payload to any to resolve TS union mismatch with generic DB interface
      await this.executeAction(item.action, item.payload as any);

      // Success: Remove from queue
      await db.syncQueue.delete(item.id);
    } catch (error) {
      // Increment retry count
      const retries = item.retries + 1;

      if (retries >= 3) {
        // Max retries reached, mark as failed
        await db.syncQueue.update(item.id, {
          status: 'failed',
          retries,
          lastError: String(error),
        });
      } else {
        // Retry later, mark as pending
        await db.syncQueue.update(item.id, {
          status: 'pending',
          retries,
        });
      }
    }
  }

  /**
   * Execute the actual action based on type
   * For MVP: All actions execute locally
   * Post-MVP: Will also sync to server
   *
   * @param action - The action type
   * @param payload - The action payload
   * @throws Error if the action fails (draft not found, etc.)
   */
  /**
   * Execute the actual action based on type
   * For MVP: SIMULATES server sync (waits 500ms then succeeds)
   * This proves infrastructure works without corrupting local DB with duplicates
   *
   * @param action - The action type
   * @param payload - The action payload
   */
  private static async executeAction(
    action: SyncAction,
    payload: SaveDraftPayload | DeleteDraftPayload | CompleteDraftPayload
  ): Promise<void> {
    // Validate action type first (required for tests and sanity)
    switch (action) {
      case 'saveDraft':
      case 'deleteDraft':
      case 'completeDraft':
        // Valid actions - proceed to simulation
        break;
      default:
        throw new Error(`Unknown action: ${action}`);
    }

    // MVP Implementation: Simulate server sync
    // We do NOT call DraftService because the action was already applied locally
    // Calling it again would create duplicates (for save) or fail (for delete)

    console.log(`[SyncManager] Simulating server sync for: ${action}`, payload);

    // Simulate network latency
    await new Promise(resolve => setTimeout(resolve, 800));
  }

  /**
   * Check network status
   * Uses browser's navigator.onLine API
   *
   * @returns true if online, false if offline
   */
  static isOnline(): boolean {
    if (typeof navigator === 'undefined') {
      return true; // SSR fallback
    }
    return navigator.onLine;
  }

  /**
   * Start listening for network changes
   * Automatically processes queue when coming back online
   */
  static startNetworkListener(): void {
    if (typeof window === 'undefined') {
      return; // SSR: no window object
    }

    window.addEventListener('online', () => {
      this.processQueue().catch(console.error);
    });
  }
}
