/**
 * Sync Manager - Handle offline data sync and storage
 * Uses IndexedDB for offline storage and Background Sync API for queued syncs
 */

class SyncManager {
  constructor() {
    this.db = null;
    this.isOnline = navigator.onLine;
    this.pendingSync = [];
    this.syncInProgress = false;

    this.init();
  }

  /**
   * Initialize Sync Manager
   */
  async init() {
    console.log('[SyncManager] Initializing...');
    
    await this.openDatabase();
    this.setupEventListeners();
    
    // Sync any pending operations on initialization
    if (this.isOnline) {
      await this.syncPendingOperations();
    }

    console.log('[SyncManager] Initialized');
  }

  /**
   * Open IndexedDB
   */
  openDatabase() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open('InvCounterDB', 2);

      request.onerror = () => {
        console.error('[SyncManager] Failed to open database:', request.error);
        reject(request.error);
      };

      request.onsuccess = () => {
        this.db = request.result;
        console.log('[SyncManager] Database opened');
        resolve(this.db);
      };

      request.onupgradeneeded = (event) => {
        const db = event.target.result;

        // Create object stores if they don't exist
        const stores = [
          { name: 'counts', keyPath: 'id' },
          { name: 'assignments', keyPath: 'id' },
          { name: 'reports', keyPath: 'id' },
          { name: 'pending_syncs', keyPath: 'id', autoIncrement: true },
          { name: 'user_data', keyPath: 'key' },
        ];

        stores.forEach((store) => {
          if (!db.objectStoreNames.contains(store.name)) {
            const os = db.createObjectStore(store.name, {
              keyPath: store.keyPath,
              autoIncrement: store.autoIncrement,
            });
            if (store.name === 'pending_syncs') {
              os.createIndex('timestamp', 'timestamp', { unique: false });
              os.createIndex('type', 'type', { unique: false });
            }
          }
        });
      };
    });
  }

  /**
   * Setup event listeners
   */
  setupEventListeners() {
    window.addEventListener('online', () => {
      console.log('[SyncManager] Online detected');
      this.isOnline = true;
      this.syncPendingOperations();
    });

    window.addEventListener('offline', () => {
      console.log('[SyncManager] Offline detected');
      this.isOnline = false;
    });
  }

  /**
   * Save a count entry for later sync
   */
  async saveCount(countData) {
    try {
      const tx = this.db.transaction(['counts', 'pending_syncs'], 'readwrite');
      const countStore = tx.objectStore('counts');
      const syncStore = tx.objectStore('pending_syncs');

      // Save count
      const countId = await this.promiseTransaction(
        countStore.put({
          id: countData.id || new Date().getTime(),
          ...countData,
          synced: false,
          timestamp: new Date().toISOString(),
        })
      );

      // Add to pending syncs
      await this.promiseTransaction(
        syncStore.add({
          type: 'count',
          itemId: countId,
          timestamp: new Date().getTime(),
          synced: false,
        })
      );

      console.log('[SyncManager] Count saved:', countId);

      // Try to sync immediately if online
      if (this.isOnline) {
        await this.syncPendingOperations();
      } else {
        // Register background sync
        if ('serviceWorker' in navigator && window.pwaHelper?.swRegistration) {
          try {
            await window.pwaHelper.swRegistration.sync.register('sync-counts');
          } catch (err) {
            console.warn('[SyncManager] Background sync registration failed:', err);
          }
        }
      }

      return countId;
    } catch (error) {
      console.error('[SyncManager] Error saving count:', error);
      throw error;
    }
  }

  /**
   * Save assignment data for offline access
   */
  async saveAssignments(assignments) {
    try {
      const tx = this.db.transaction('assignments', 'readwrite');
      const store = tx.objectStore('assignments');

      // Clear existing assignments
      await this.promiseTransaction(store.clear());

      // Add new assignments
      assignments.forEach((assignment) => {
        store.add({
          ...assignment,
          cachedAt: new Date().toISOString(),
        });
      });

      await new Promise((resolve, reject) => {
        tx.oncomplete = resolve;
        tx.onerror = () => reject(tx.error);
      });

      console.log('[SyncManager] Saved', assignments.length, 'assignments');
      return true;
    } catch (error) {
      console.error('[SyncManager] Error saving assignments:', error);
      throw error;
    }
  }

  /**
   * Save report data for offline access
   */
  async saveReports(reports) {
    try {
      const tx = this.db.transaction('reports', 'readwrite');
      const store = tx.objectStore('reports');

      // Clear existing reports
      await this.promiseTransaction(store.clear());

      // Add new reports
      reports.forEach((report) => {
        store.add({
          ...report,
          cachedAt: new Date().toISOString(),
        });
      });

      await new Promise((resolve, reject) => {
        tx.oncomplete = resolve;
        tx.onerror = () => reject(tx.error);
      });

      console.log('[SyncManager] Saved', reports.length, 'reports');
      return true;
    } catch (error) {
      console.error('[SyncManager] Error saving reports:', error);
      throw error;
    }
  }

  /**
   * Get pending counts for sync
   */
  async getPendingCounts() {
    try {
      const tx = this.db.transaction('counts', 'readonly');
      const store = tx.objectStore('counts');

      return await new Promise((resolve, reject) => {
        const request = store.getAll();
        request.onerror = () => reject(request.error);
        request.onsuccess = () => resolve(request.result.filter((c) => !c.synced));
      });
    } catch (error) {
      console.error('[SyncManager] Error getting pending counts:', error);
      return [];
    }
  }

  /**
   * Get cached assignments
   */
  async getAssignments() {
    try {
      const tx = this.db.transaction('assignments', 'readonly');
      const store = tx.objectStore('assignments');

      return await new Promise((resolve, reject) => {
        const request = store.getAll();
        request.onerror = () => reject(request.error);
        request.onsuccess = () => resolve(request.result);
      });
    } catch (error) {
      console.error('[SyncManager] Error getting assignments:', error);
      return [];
    }
  }

  /**
   * Get cached reports
   */
  async getReports() {
    try {
      const tx = this.db.transaction('reports', 'readonly');
      const store = tx.objectStore('reports');

      return await new Promise((resolve, reject) => {
        const request = store.getAll();
        request.onerror = () => reject(request.error);
        request.onsuccess = () => resolve(request.result);
      });
    } catch (error) {
      console.error('[SyncManager] Error getting reports:', error);
      return [];
    }
  }

  /**
   * Sync pending operations to server
   */
  async syncPendingOperations() {
    if (this.syncInProgress || !this.isOnline) {
      console.log('[SyncManager] Sync already in progress or offline');
      return;
    }

    this.syncInProgress = true;

    try {
      console.log('[SyncManager] Starting sync...');

      // Get pending counts
      const pendingCounts = await this.getPendingCounts();
      if (pendingCounts.length > 0) {
        await this.syncCounts(pendingCounts);
      }

      // Fetch fresh data from server
      await this.fetchAssignments();
      await this.fetchReports();

      console.log('[SyncManager] Sync completed');
    } catch (error) {
      console.error('[SyncManager] Sync error:', error);
    } finally {
      this.syncInProgress = false;
    }
  }

  /**
   * Sync counts to server
   */
  async syncCounts(counts) {
    try {
      console.log('[SyncManager] Syncing', counts.length, 'counts...');

      const response = await fetch('/invcount/api/sync-counts', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ counts }),
      });

      if (!response.ok) {
        throw new Error('Sync failed: ' + response.status);
      }

      const result = await response.json();

      // Mark counts as synced
      const tx = this.db.transaction('counts', 'readwrite');
      const store = tx.objectStore('counts');

      for (const countId of result.synced_ids || []) {
        const count = await this.promiseTransaction(store.get(countId));
        if (count) {
          count.synced = true;
          await this.promiseTransaction(store.put(count));
        }
      }

      console.log('[SyncManager] Count sync completed');
      return result;
    } catch (error) {
      console.error('[SyncManager] Error syncing counts:', error);
      throw error;
    }
  }

  /**
   * Fetch assignments from server and cache
   */
  async fetchAssignments() {
    try {
      console.log('[SyncManager] Fetching assignments...');

      const response = await fetch('/invcount/api/get-assignments');
      if (!response.ok) {
        throw new Error('Failed to fetch assignments: ' + response.status);
      }

      const assignments = await response.json();
      await this.saveAssignments(assignments);

      console.log('[SyncManager] Assignments fetched:', assignments.length);
      return assignments;
    } catch (error) {
      console.error('[SyncManager] Error fetching assignments:', error);
      // Return cached assignments if available
      return await this.getAssignments();
    }
  }

  /**
   * Fetch reports from server and cache
   */
  async fetchReports() {
    try {
      console.log('[SyncManager] Fetching reports...');

      const response = await fetch('/invcount/api/get-reports');
      if (!response.ok) {
        throw new Error('Failed to fetch reports: ' + response.status);
      }

      const reports = await response.json();
      await this.saveReports(reports);

      console.log('[SyncManager] Reports fetched:', reports.length);
      return reports;
    } catch (error) {
      console.error('[SyncManager] Error fetching reports:', error);
      // Return cached reports if available
      return await this.getReports();
    }
  }

  /**
   * Utility: Convert transaction request to promise
   */
  promiseTransaction(request) {
    return new Promise((resolve, reject) => {
      request.onerror = () => reject(request.error);
      request.onsuccess = () => resolve(request.result);
    });
  }

  /**
   * Clear all cached data
   */
  async clearCache() {
    try {
      const stores = ['counts', 'assignments', 'reports', 'pending_syncs'];
      const tx = this.db.transaction(stores, 'readwrite');

      stores.forEach((storeName) => {
        tx.objectStore(storeName).clear();
      });

      await new Promise((resolve, reject) => {
        tx.oncomplete = resolve;
        tx.onerror = () => reject(tx.error);
      });

      console.log('[SyncManager] Cache cleared');
      return true;
    } catch (error) {
      console.error('[SyncManager] Error clearing cache:', error);
      throw error;
    }
  }

  /**
   * Get sync status
   */
  getSyncStatus() {
    return {
      isOnline: this.isOnline,
      syncInProgress: this.syncInProgress,
      databaseReady: !!this.db,
    };
  }
}

// Initialize Sync Manager when DOM is ready
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', () => {
    window.syncManager = new SyncManager();
  });
} else {
  window.syncManager = new SyncManager();
}
