import { OfflineQueueItem } from '@/types';
import { encryptPayload, decryptPayload } from './offlineCrypto';

const DB_NAME = 'ElectNG_Offline_DB';
const DB_VERSION = 1;
const STORE_NAME = 'pending_submissions';
const LOCAL_STORAGE_KEY = 'electng_offline_queue_backup';

// Initialize IndexedDB with fallback support
function openDatabase(): Promise<IDBDatabase | null> {
  if (typeof window === 'undefined' || !window.indexedDB) {
    return Promise.resolve(null);
  }

  return new Promise((resolve) => {
    try {
      const request = window.indexedDB.open(DB_NAME, DB_VERSION);
      request.onerror = () => resolve(null);
      request.onsuccess = () => resolve(request.result);
      request.onupgradeneeded = (event: any) => {
        const db = event.target.result;
        if (!db.objectStoreNames.contains(STORE_NAME)) {
          db.createObjectStore(STORE_NAME, { keyPath: 'id' });
        }
      };
    } catch {
      resolve(null);
    }
  });
}

// Fallback LocalStorage Queue
function getLocalQueue(): OfflineQueueItem[] {
  if (typeof window === 'undefined') return [];
  try {
    const raw = localStorage.getItem(LOCAL_STORAGE_KEY);
    return raw ? JSON.parse(raw) : [];
  } catch {
    return [];
  }
}

function saveLocalQueue(items: OfflineQueueItem[]): void {
  if (typeof window === 'undefined') return;
  try {
    localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(items));
  } catch (e) {
    console.warn('LocalStorage queue error:', e);
  }
}

// Enqueue item with AES-GCM encryption
export async function enqueueOfflineItem(
  type: OfflineQueueItem['type'],
  payload: any
): Promise<OfflineQueueItem> {
  // Encrypt payload before persisting to disk
  const encryptedPayload = await encryptPayload(payload);

  const storageItem: any = {
    id: `off-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`,
    type,
    payload: encryptedPayload,
    is_encrypted: true,
    created_at: new Date().toISOString(),
    retry_count: 0,
  };

  const db = await openDatabase();
  if (db) {
    const tx = db.transaction(STORE_NAME, 'readwrite');
    tx.objectStore(STORE_NAME).put(storageItem);
  }

  const currentLocal = getLocalQueue();
  currentLocal.push(storageItem);
  saveLocalQueue(currentLocal);

  return {
    ...storageItem,
    payload,
    is_encrypted: false,
  };
}

// Retrieve and decrypt queued items
export async function getOfflineQueue(): Promise<OfflineQueueItem[]> {
  const db = await openDatabase();
  let rawItems: any[] = [];

  if (db) {
    rawItems = await new Promise<any[]>((resolve) => {
      const tx = db.transaction(STORE_NAME, 'readonly');
      const request = tx.objectStore(STORE_NAME).getAll();
      request.onsuccess = () => resolve(request.result || []);
      request.onerror = () => resolve([]);
    });
  }

  if (rawItems.length === 0) {
    rawItems = getLocalQueue();
  }

  // Decrypt items in memory
  const decryptedItems = await Promise.all(
    rawItems.map(async (item) => {
      if (item.is_encrypted && typeof item.payload === 'string') {
        try {
          const decryptedPayload = await decryptPayload(item.payload);
          return {
            ...item,
            payload: decryptedPayload,
            is_encrypted: false,
          };
        } catch (e) {
          console.error('Failed to decrypt offline queue item:', item.id, e);
          return item;
        }
      }
      return item;
    })
  );

  return decryptedItems;
}

// Remove item from queue after successful sync
export async function dequeueOfflineItem(id: string): Promise<void> {
  const db = await openDatabase();
  if (db) {
    const tx = db.transaction(STORE_NAME, 'readwrite');
    tx.objectStore(STORE_NAME).delete(id);
  }
  const filtered = getLocalQueue().filter((item) => item.id !== id);
  saveLocalQueue(filtered);
}

// Clear all offline queue items (used for zero-baseline resetting)
export async function clearOfflineQueue(): Promise<void> {
  const db = await openDatabase();
  if (db) {
    const tx = db.transaction(STORE_NAME, 'readwrite');
    tx.objectStore(STORE_NAME).clear();
  }
  if (typeof window !== 'undefined') {
    localStorage.removeItem(LOCAL_STORAGE_KEY);
  }
}

// Clear all module drafts
export function clearAllModuleDrafts(): void {
  if (typeof window === 'undefined') return;
  try {
    const keysToRemove: string[] = [];
    for (let i = 0; i < localStorage.length; i++) {
      const key = localStorage.key(i);
      if (key && key.startsWith('draft_')) {
        keysToRemove.push(key);
      }
    }
    keysToRemove.forEach((key) => localStorage.removeItem(key));
  } catch (err) {
    console.warn('Failed to clear drafts:', err);
  }
}

// Module Draft Autosave with Obfuscated Payload (survives app reload / phone battery loss)
export function saveModuleDraft(puCode: string, moduleKey: string, data: any): void {
  if (typeof window === 'undefined') return;
  try {
    const jsonString = JSON.stringify(data);
    const encoded = 'enc:' + btoa(unescape(encodeURIComponent(jsonString)));
    localStorage.setItem(`draft_${puCode}_${moduleKey}`, encoded);
  } catch (err) {
    console.warn('Failed to save draft securely:', err);
  }
}

export function getModuleDraft<T>(puCode: string, moduleKey: string): T | null {
  if (typeof window === 'undefined') return null;
  try {
    const raw = localStorage.getItem(`draft_${puCode}_${moduleKey}`);
    if (!raw) return null;
    if (raw.startsWith('enc:')) {
      const jsonString = decodeURIComponent(escape(atob(raw.slice(4))));
      return JSON.parse(jsonString);
    }
    return JSON.parse(raw);
  } catch {
    return null;
  }
}
