/**
 * Offline Support Utilities
 * Provides IndexedDB storage and background sync for offline functionality
 */

import { openDB, DBSchema, IDBPDatabase } from 'idb';

// Database schema
interface LocalGreenChainDB extends DBSchema {
  'pending-plants': {
    key: string;
    value: {
      id: string;
      data: any;
      createdAt: string;
      attempts: number;
    };
    indexes: { 'by-created': string };
  };
  'pending-transport': {
    key: string;
    value: {
      id: string;
      data: any;
      createdAt: string;
      attempts: number;
    };
    indexes: { 'by-created': string };
  };
  'cached-plants': {
    key: string;
    value: {
      id: string;
      data: any;
      cachedAt: string;
    };
    indexes: { 'by-cached': string };
  };
  'user-preferences': {
    key: string;
    value: any;
  };
}

const DB_NAME = 'localgreenchain-offline';
const DB_VERSION = 1;

let dbPromise: Promise<IDBPDatabase<LocalGreenChainDB>> | null = null;

async function getDB(): Promise<IDBPDatabase<LocalGreenChainDB>> {
  if (!dbPromise) {
    dbPromise = openDB<LocalGreenChainDB>(DB_NAME, DB_VERSION, {
      upgrade(db) {
        // Pending plants store
        if (!db.objectStoreNames.contains('pending-plants')) {
          const plantStore = db.createObjectStore('pending-plants', { keyPath: 'id' });
          plantStore.createIndex('by-created', 'createdAt');
        }

        // Pending transport store
        if (!db.objectStoreNames.contains('pending-transport')) {
          const transportStore = db.createObjectStore('pending-transport', { keyPath: 'id' });
          transportStore.createIndex('by-created', 'createdAt');
        }

        // Cached plants store
        if (!db.objectStoreNames.contains('cached-plants')) {
          const cacheStore = db.createObjectStore('cached-plants', { keyPath: 'id' });
          cacheStore.createIndex('by-cached', 'cachedAt');
        }

        // User preferences store
        if (!db.objectStoreNames.contains('user-preferences')) {
          db.createObjectStore('user-preferences');
        }
      },
    });
  }
  return dbPromise;
}

// Network status
export function isOnline(): boolean {
  return typeof navigator !== 'undefined' ? navigator.onLine : true;
}

export function onNetworkChange(callback: (online: boolean) => void): () => void {
  if (typeof window === 'undefined') return () => {};

  const handleOnline = () => callback(true);
  const handleOffline = () => callback(false);

  window.addEventListener('online', handleOnline);
  window.addEventListener('offline', handleOffline);

  return () => {
    window.removeEventListener('online', handleOnline);
    window.removeEventListener('offline', handleOffline);
  };
}

// Pending operations
export async function queuePlantRegistration(plantData: any): Promise<string> {
  const db = await getDB();
  const id = generateId();

  await db.put('pending-plants', {
    id,
    data: plantData,
    createdAt: new Date().toISOString(),
    attempts: 0,
  });

  // Register for background sync if available
  if ('serviceWorker' in navigator && 'sync' in ServiceWorkerRegistration.prototype) {
    const registration = await navigator.serviceWorker.ready;
    await (registration as any).sync.register('sync-plants');
  }

  return id;
}

export async function queueTransportEvent(eventData: any): Promise<string> {
  const db = await getDB();
  const id = generateId();

  await db.put('pending-transport', {
    id,
    data: eventData,
    createdAt: new Date().toISOString(),
    attempts: 0,
  });

  // Register for background sync if available
  if ('serviceWorker' in navigator && 'sync' in ServiceWorkerRegistration.prototype) {
    const registration = await navigator.serviceWorker.ready;
    await (registration as any).sync.register('sync-transport');
  }

  return id;
}

export async function getPendingPlants(): Promise<any[]> {
  const db = await getDB();
  return db.getAll('pending-plants');
}

export async function getPendingTransport(): Promise<any[]> {
  const db = await getDB();
  return db.getAll('pending-transport');
}

export async function removePendingPlant(id: string): Promise<void> {
  const db = await getDB();
  await db.delete('pending-plants', id);
}

export async function removePendingTransport(id: string): Promise<void> {
  const db = await getDB();
  await db.delete('pending-transport', id);
}

// Plant caching
export async function cachePlant(plant: any): Promise<void> {
  const db = await getDB();
  await db.put('cached-plants', {
    id: plant.id,
    data: plant,
    cachedAt: new Date().toISOString(),
  });
}

export async function getCachedPlant(id: string): Promise<any | null> {
  const db = await getDB();
  const cached = await db.get('cached-plants', id);
  return cached?.data || null;
}

export async function getCachedPlants(): Promise<any[]> {
  const db = await getDB();
  const all = await db.getAll('cached-plants');
  return all.map((item) => item.data);
}

export async function clearCachedPlants(): Promise<void> {
  const db = await getDB();
  await db.clear('cached-plants');
}

// User preferences
export async function setPreference(key: string, value: any): Promise<void> {
  const db = await getDB();
  await db.put('user-preferences', value, key);
}

export async function getPreference<T>(key: string): Promise<T | null> {
  const db = await getDB();
  return db.get('user-preferences', key) as Promise<T | null>;
}

// Sync operations
export async function syncPendingPlants(): Promise<{ success: number; failed: number }> {
  const pending = await getPendingPlants();
  let success = 0;
  let failed = 0;

  for (const item of pending) {
    try {
      const response = await fetch('/api/plants/register', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(item.data),
      });

      if (response.ok) {
        await removePendingPlant(item.id);
        success++;
      } else {
        failed++;
      }
    } catch {
      failed++;
    }
  }

  return { success, failed };
}

export async function syncPendingTransport(): Promise<{ success: number; failed: number }> {
  const pending = await getPendingTransport();
  let success = 0;
  let failed = 0;

  for (const item of pending) {
    try {
      const response = await fetch('/api/transport/events', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(item.data),
      });

      if (response.ok) {
        await removePendingTransport(item.id);
        success++;
      } else {
        failed++;
      }
    } catch {
      failed++;
    }
  }

  return { success, failed };
}

export async function syncAll(): Promise<void> {
  if (!isOnline()) return;

  await Promise.all([
    syncPendingPlants(),
    syncPendingTransport(),
  ]);
}

// Utility
function generateId(): string {
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}

// Auto-sync when coming online
if (typeof window !== 'undefined') {
  window.addEventListener('online', () => {
    syncAll().catch(console.error);
  });
}
