/**
 * Design System Server (DSS) - IndexedDB Storage
 *
 * Browser-side storage for offline-first operation:
 * - Projects, components, tokens (synced from backend)
 * - UI state persistence
 * - Request queue for offline operations
 *
 * Uses IndexedDB for structured data with good performance.
 */

const DB_NAME = 'dss';
const DB_VERSION = 2;

class DssDB {
  constructor() {
    this.db = null;
    this.ready = this.init();
  }

  // === Initialization ===

  async init() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(DB_NAME, DB_VERSION);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        console.log('[DssDB] Database ready');
        resolve(this.db);
      };

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

  createStores(db) {
    // Projects
    if (!db.objectStoreNames.contains('projects')) {
      const store = db.createObjectStore('projects', { keyPath: 'id' });
      store.createIndex('status', 'status', { unique: false });
      store.createIndex('updated_at', 'updated_at', { unique: false });
    }

    // Components
    if (!db.objectStoreNames.contains('components')) {
      const store = db.createObjectStore('components', { keyPath: 'id' });
      store.createIndex('project_id', 'project_id', { unique: false });
      store.createIndex('name', 'name', { unique: false });
    }

    // Tokens
    if (!db.objectStoreNames.contains('tokens')) {
      const store = db.createObjectStore('tokens', { keyPath: 'id' });
      store.createIndex('project_id', 'project_id', { unique: false });
      store.createIndex('category', 'category', { unique: false });
    }

    // Styles
    if (!db.objectStoreNames.contains('styles')) {
      const store = db.createObjectStore('styles', { keyPath: 'id' });
      store.createIndex('project_id', 'project_id', { unique: false });
      store.createIndex('type', 'type', { unique: false });
    }

    // Sync Queue (offline operations)
    if (!db.objectStoreNames.contains('sync_queue')) {
      const store = db.createObjectStore('sync_queue', { keyPath: 'id', autoIncrement: true });
      store.createIndex('status', 'status', { unique: false });
      store.createIndex('created_at', 'created_at', { unique: false });
    }

    // Activity Log
    if (!db.objectStoreNames.contains('activity')) {
      const store = db.createObjectStore('activity', { keyPath: 'id', autoIncrement: true });
      store.createIndex('project_id', 'project_id', { unique: false });
      store.createIndex('created_at', 'created_at', { unique: false });
    }

    // Cache (with TTL)
    if (!db.objectStoreNames.contains('cache')) {
      const store = db.createObjectStore('cache', { keyPath: 'key' });
      store.createIndex('expires_at', 'expires_at', { unique: false });
    }

    // UI State
    if (!db.objectStoreNames.contains('ui_state')) {
      db.createObjectStore('ui_state', { keyPath: 'key' });
    }

    // Notifications
    if (!db.objectStoreNames.contains('notifications')) {
      const store = db.createObjectStore('notifications', { keyPath: 'id' });
      store.createIndex('read', 'read', { unique: false });
      store.createIndex('timestamp', 'timestamp', { unique: false });
      store.createIndex('type', 'type', { unique: false });
    }

    console.log('[DssDB] Stores created');
  }

  // === Generic Operations ===

  async transaction(storeName, mode = 'readonly') {
    await this.ready;
    return this.db.transaction(storeName, mode).objectStore(storeName);
  }

  async put(storeName, data) {
    await this.ready;
    return new Promise((resolve, reject) => {
      const tx = this.db.transaction(storeName, 'readwrite');
      const store = tx.objectStore(storeName);
      const request = store.put(data);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async get(storeName, key) {
    await this.ready;
    return new Promise((resolve, reject) => {
      const tx = this.db.transaction(storeName, 'readonly');
      const store = tx.objectStore(storeName);
      const request = store.get(key);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getAll(storeName) {
    await this.ready;
    return new Promise((resolve, reject) => {
      const tx = this.db.transaction(storeName, 'readonly');
      const store = tx.objectStore(storeName);
      const request = store.getAll();
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getAllByIndex(storeName, indexName, value) {
    await this.ready;
    return new Promise((resolve, reject) => {
      const tx = this.db.transaction(storeName, 'readonly');
      const store = tx.objectStore(storeName);
      const index = store.index(indexName);
      const request = index.getAll(value);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async delete(storeName, key) {
    await this.ready;
    return new Promise((resolve, reject) => {
      const tx = this.db.transaction(storeName, 'readwrite');
      const store = tx.objectStore(storeName);
      const request = store.delete(key);
      request.onsuccess = () => resolve(true);
      request.onerror = () => reject(request.error);
    });
  }

  async clear(storeName) {
    await this.ready;
    return new Promise((resolve, reject) => {
      const tx = this.db.transaction(storeName, 'readwrite');
      const store = tx.objectStore(storeName);
      const request = store.clear();
      request.onsuccess = () => resolve(true);
      request.onerror = () => reject(request.error);
    });
  }

  async count(storeName) {
    await this.ready;
    return new Promise((resolve, reject) => {
      const tx = this.db.transaction(storeName, 'readonly');
      const store = tx.objectStore(storeName);
      const request = store.count();
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  // === Cache with TTL ===

  async cacheSet(key, value, ttlSeconds = 300) {
    const data = {
      key,
      value,
      created_at: Date.now(),
      expires_at: Date.now() + (ttlSeconds * 1000)
    };
    return this.put('cache', data);
  }

  async cacheGet(key) {
    const data = await this.get('cache', key);
    if (!data) return null;

    // Check expiration
    if (Date.now() > data.expires_at) {
      await this.delete('cache', key);
      return null;
    }

    return data.value;
  }

  async cacheClear() {
    return this.clear('cache');
  }

  async cacheCleanExpired() {
    await this.ready;
    const now = Date.now();

    return new Promise((resolve, reject) => {
      const tx = this.db.transaction('cache', 'readwrite');
      const store = tx.objectStore('cache');
      const index = store.index('expires_at');
      const range = IDBKeyRange.upperBound(now);

      let count = 0;
      const request = index.openCursor(range);

      request.onsuccess = (event) => {
        const cursor = event.target.result;
        if (cursor) {
          store.delete(cursor.primaryKey);
          count++;
          cursor.continue();
        } else {
          resolve(count);
        }
      };

      request.onerror = () => reject(request.error);
    });
  }

  // === Projects ===

  async saveProject(project) {
    return this.put('projects', {
      ...project,
      updated_at: new Date().toISOString()
    });
  }

  async getProject(id) {
    return this.get('projects', id);
  }

  async getProjects() {
    return this.getAll('projects');
  }

  // === Components ===

  async saveComponents(projectId, components) {
    for (const comp of components) {
      await this.put('components', {
        id: comp.id || `${projectId}-${comp.name}`,
        project_id: projectId,
        ...comp
      });
    }
    return components.length;
  }

  async getComponents(projectId) {
    return this.getAllByIndex('components', 'project_id', projectId);
  }

  // === Tokens ===

  async saveTokens(projectId, tokens) {
    for (const token of tokens) {
      await this.put('tokens', {
        id: `${projectId}-${token.name}`,
        project_id: projectId,
        ...token
      });
    }
    return tokens.length;
  }

  async getTokens(projectId) {
    return this.getAllByIndex('tokens', 'project_id', projectId);
  }

  async getTokensByCategory(projectId, category) {
    const tokens = await this.getTokens(projectId);
    return tokens.filter(t => t.category === category);
  }

  // === Sync Queue (Offline Operations) ===

  async queueOperation(operation) {
    return this.put('sync_queue', {
      ...operation,
      status: 'pending',
      created_at: new Date().toISOString(),
      retries: 0
    });
  }

  async getPendingOperations() {
    return this.getAllByIndex('sync_queue', 'status', 'pending');
  }

  async markOperationComplete(id) {
    const op = await this.get('sync_queue', id);
    if (op) {
      op.status = 'complete';
      op.completed_at = new Date().toISOString();
      await this.put('sync_queue', op);
    }
  }

  async markOperationFailed(id, error) {
    const op = await this.get('sync_queue', id);
    if (op) {
      op.status = 'failed';
      op.error = error;
      op.retries = (op.retries || 0) + 1;
      await this.put('sync_queue', op);
    }
  }

  // === Activity Log ===

  async logActivity(action, details = {}) {
    return this.put('activity', {
      action,
      ...details,
      created_at: new Date().toISOString()
    });
  }

  async getRecentActivity(limit = 50) {
    const all = await this.getAll('activity');
    return all
      .sort((a, b) => new Date(b.created_at) - new Date(a.created_at))
      .slice(0, limit);
  }

  // === UI State ===

  async saveUIState(key, value) {
    return this.put('ui_state', { key, value });
  }

  async getUIState(key) {
    const data = await this.get('ui_state', key);
    return data?.value;
  }

  // === Stats ===

  async getStats() {
    const stores = ['projects', 'components', 'tokens', 'styles', 'activity', 'sync_queue', 'cache'];
    const stats = {};

    for (const store of stores) {
      stats[store] = await this.count(store);
    }

    // Storage estimate
    if (navigator.storage && navigator.storage.estimate) {
      const estimate = await navigator.storage.estimate();
      stats.storage_used_mb = Math.round(estimate.usage / (1024 * 1024) * 100) / 100;
      stats.storage_quota_mb = Math.round(estimate.quota / (1024 * 1024));
    }

    return stats;
  }

  // === Sync with Backend ===

  async syncFromBackend(storeName, data, keyField = 'id') {
    // Clear existing and bulk insert
    await this.clear(storeName);
    for (const item of data) {
      await this.put(storeName, item);
    }
    return data.length;
  }
}

// Singleton instance
const dssDB = new DssDB();

// Export
export { DssDB };
export default dssDB;
