/**
 * Storage utility that uses IndexedDB for table data and localStorage for config/secrets
 *
 * Strategy:
 * - IndexedDB: ALL table/datasource data (can store hundreds of MB)
 * - localStorage: Only for secrets, variables, configurations (small data)
 */

const DB_NAME = 'analitica-nucleo-db'
const DB_VERSION = 1
const STORE_NAME = 'table-data'

let dbInstance: IDBDatabase | null = null

/**
 * Initialize IndexedDB
 */
async function initDB(): Promise<IDBDatabase> {
  if (dbInstance) return dbInstance

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

    request.onerror = () => {
      console.error('[Storage] IndexedDB error:', request.error)
      reject(request.error)
    }

    request.onsuccess = () => {
      dbInstance = request.result
      console.log('[Storage] IndexedDB initialized successfully')
      resolve(dbInstance)
    }

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

      // Create object store if it doesn't exist
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        db.createObjectStore(STORE_NAME, { keyPath: 'key' })
        console.log('[Storage] Created object store:', STORE_NAME)
      }
    }
  })
}

/**
 * Deep clone to remove reactivity and make data serializable for IndexedDB
 */
function toPlainObject(obj: any): any {
  // Use JSON parse/stringify to deep clone and remove all reactivity/proxies
  try {
    return JSON.parse(JSON.stringify(obj))
  } catch (error) {
    console.error('[Storage] Failed to convert to plain object:', error)
    return obj
  }
}

/**
 * Save data to IndexedDB
 */
async function saveToIndexedDB(key: string, data: any): Promise<boolean> {
  try {
    const db = await initDB()

    // Convert reactive objects to plain objects
    const plainData = toPlainObject(data)

    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readwrite')
      const store = transaction.objectStore(STORE_NAME)

      const request = store.put({
        key,
        value: plainData,
        timestamp: Date.now()
      })

      request.onsuccess = () => {
        console.log(`[Storage] Saved to IndexedDB: ${key}`)
        resolve(true)
      }

      request.onerror = () => {
        console.error(`[Storage] Failed to save to IndexedDB: ${key}`, request.error)
        reject(request.error)
      }
    })
  } catch (error) {
    console.error('[Storage] IndexedDB save error:', error)
    return false
  }
}

/**
 * Load data from IndexedDB
 */
async function loadFromIndexedDB(key: string): Promise<any | null> {
  try {
    const db = await initDB()

    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readonly')
      const store = transaction.objectStore(STORE_NAME)
      const request = store.get(key)

      request.onsuccess = () => {
        if (request.result) {
          console.log(`[Storage] Loaded from IndexedDB: ${key}`)
          resolve(request.result.value)
        } else {
          console.log(`[Storage] No data found in IndexedDB for: ${key}`)
          resolve(null)
        }
      }

      request.onerror = () => {
        console.error(`[Storage] Failed to load from IndexedDB: ${key}`, request.error)
        reject(request.error)
      }
    })
  } catch (error) {
    console.error('[Storage] IndexedDB load error:', error)
    return null
  }
}

/**
 * Remove data from IndexedDB
 */
async function removeFromIndexedDB(key: string): Promise<boolean> {
  try {
    const db = await initDB()

    return new Promise((resolve, reject) => {
      const transaction = db.transaction([STORE_NAME], 'readwrite')
      const store = transaction.objectStore(STORE_NAME)
      const request = store.delete(key)

      request.onsuccess = () => {
        console.log(`[Storage] Removed from IndexedDB: ${key}`)
        resolve(true)
      }

      request.onerror = () => {
        console.error(`[Storage] Failed to remove from IndexedDB: ${key}`, request.error)
        reject(request.error)
      }
    })
  } catch (error) {
    console.error('[Storage] IndexedDB remove error:', error)
    return false
  }
}

/**
 * Storage interface for table/datasource data (uses IndexedDB exclusively)
 */
export const tableDataStorage = {
  /**
   * Save table data to IndexedDB (for large datasets)
   */
  async setItem(key: string, data: any): Promise<void> {
    if (!process.client) {
      console.log('[TableDataStorage] Not on client, skipping storage')
      return
    }

    const dataString = JSON.stringify(data)
    const dataSizeKB = new Blob([dataString]).size / 1024
    console.log(`[TableDataStorage] Saving ${dataSizeKB.toFixed(2)} KB to IndexedDB for key: ${key}`)

    try {
      const success = await saveToIndexedDB(key, data)
      if (success) {
        console.log(`[TableDataStorage] ✓ Successfully saved ${dataSizeKB.toFixed(2)} KB to IndexedDB`)
        return
      } else {
        throw new Error('Failed to save to IndexedDB')
      }
    } catch (error) {
      console.error('[TableDataStorage] ✗ Failed to save to IndexedDB:', error)
      throw new Error(`No se pudo guardar la tabla: ${error}`)
    }
  },

  /**
   * Load table data from IndexedDB
   */
  async getItem(key: string): Promise<any | null> {
    if (!process.client) {
      console.log('[TableDataStorage] Not on client, skipping storage')
      return null
    }

    console.log(`[TableDataStorage] Loading from IndexedDB: ${key}`)

    try {
      const data = await loadFromIndexedDB(key)
      if (data !== null) {
        const dataString = JSON.stringify(data)
        const dataSizeKB = new Blob([dataString]).size / 1024
        console.log(`[TableDataStorage] ✓ Loaded ${dataSizeKB.toFixed(2)} KB from IndexedDB`)
        return data
      } else {
        console.log(`[TableDataStorage] No data found in IndexedDB for: ${key}`)
        return null
      }
    } catch (error) {
      console.error('[TableDataStorage] ✗ Failed to load from IndexedDB:', error)
      return null
    }
  },

  /**
   * Remove table data from IndexedDB
   */
  async removeItem(key: string): Promise<void> {
    if (!process.client) return

    console.log(`[TableDataStorage] Removing from IndexedDB: ${key}`)

    try {
      await removeFromIndexedDB(key)
      console.log(`[TableDataStorage] ✓ Removed from IndexedDB: ${key}`)
    } catch (error) {
      console.error('[TableDataStorage] ✗ Failed to remove from IndexedDB:', error)
    }
  }
}

/**
 * Storage interface for config/secrets (uses localStorage exclusively)
 */
export const configStorage = {
  /**
   * Save config/secrets to localStorage (for small data only)
   */
  setItem(key: string, data: any): void {
    if (!process.client) {
      console.log('[ConfigStorage] Not on client, skipping storage')
      return
    }

    const dataString = JSON.stringify(data)
    const dataSizeKB = new Blob([dataString]).size / 1024
    console.log(`[ConfigStorage] Saving ${dataSizeKB.toFixed(2)} KB to localStorage for key: ${key}`)

    try {
      localStorage.setItem(key, dataString)
      console.log(`[ConfigStorage] ✓ Saved to localStorage`)
    } catch (error: any) {
      if (error.name === 'QuotaExceededError') {
        console.error('[ConfigStorage] ✗ localStorage quota exceeded')
        throw new Error('No hay espacio en localStorage para guardar la configuración')
      }
      throw error
    }
  },

  /**
   * Load config/secrets from localStorage
   */
  getItem(key: string): any | null {
    if (!process.client) {
      console.log('[ConfigStorage] Not on client, skipping storage')
      return null
    }

    console.log(`[ConfigStorage] Loading from localStorage: ${key}`)

    try {
      const item = localStorage.getItem(key)
      if (item) {
        const data = JSON.parse(item)
        const dataSizeKB = new Blob([item]).size / 1024
        console.log(`[ConfigStorage] ✓ Loaded ${dataSizeKB.toFixed(2)} KB from localStorage`)
        return data
      } else {
        console.log(`[ConfigStorage] No data found in localStorage for: ${key}`)
        return null
      }
    } catch (error) {
      console.error('[ConfigStorage] ✗ Failed to load from localStorage:', error)
      return null
    }
  },

  /**
   * Remove config/secrets from localStorage
   */
  removeItem(key: string): void {
    if (!process.client) return

    console.log(`[ConfigStorage] Removing from localStorage: ${key}`)

    try {
      localStorage.removeItem(key)
      console.log(`[ConfigStorage] ✓ Removed from localStorage: ${key}`)
    } catch (error) {
      console.error('[ConfigStorage] ✗ Failed to remove from localStorage:', error)
    }
  }
}