/**
 * Composable para manejo de sesiones de catación en IndexedDB
 * Maneja solo una sesión activa a la vez
 */

import type { SesionCatacion } from '~/types/catacion'

const DB_NAME = 'RioCataDB'
const DB_VERSION = 1
const STORE_NAME = 'sesiones'
const ACTIVE_SESSION_KEY = 'sesion-activa'

/**
 * Inicializa y retorna la base de datos IndexedDB
 */
function openDatabase(): Promise<IDBDatabase> {
  return new Promise((resolve, reject) => {
    // Verificar si estamos en el cliente
    if (typeof window === 'undefined' || !window.indexedDB) {
      reject(new Error('IndexedDB no está disponible en este entorno'))
      return
    }

    const request = window.indexedDB.open(DB_NAME, DB_VERSION)

    request.onerror = () => {
      reject(new Error('Error al abrir la base de datos'))
    }

    request.onsuccess = () => {
      resolve(request.result)
    }

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

      // Crear object store si no existe
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        const objectStore = db.createObjectStore(STORE_NAME, { keyPath: 'sessionId' })
        // Crear índice por fecha para consultas futuras
        objectStore.createIndex('fecha', 'fecha', { unique: false })
      }
    }
  })
}

/**
 * Guarda o actualiza la sesión activa en IndexedDB
 */
async function saveSession(sesion: SesionCatacion): Promise<void> {
  try {
    // Usar toRaw para convertir proxies reactivos de Vue a objetos planos
    // antes de guardar en IndexedDB, pero necesitamos clonar profundamente
    // para evitar referencias a objetos reactivos
    const { toRaw } = await import('vue')
    const sesionPlana = JSON.parse(JSON.stringify(toRaw(sesion))) as SesionCatacion

    const db = await openDatabase()
    const transaction = db.transaction([STORE_NAME], 'readwrite')
    const objectStore = transaction.objectStore(STORE_NAME)

    // Eliminar todas las sesiones anteriores
    await new Promise<void>((resolve, reject) => {
      const clearRequest = objectStore.clear()
      clearRequest.onsuccess = () => resolve()
      clearRequest.onerror = () => reject(new Error('Error al limpiar sesiones anteriores'))
    })

    // Guardar la nueva sesión
    await new Promise<void>((resolve, reject) => {
      const addRequest = objectStore.add(sesionPlana)
      addRequest.onsuccess = () => resolve()
      addRequest.onerror = () => reject(new Error('Error al guardar la sesión'))
    })

    db.close()
  } catch (error) {
    console.error('Error en saveSession:', error)
    throw error
  }
}

/**
 * Migra una sesión del formato antiguo al nuevo
 */
function migrateSesion(sesion: any): SesionCatacion {
  // Migrar cada muestra
  const muestrasMigradas = sesion.muestras.map((muestra: any) => {
    const muestraMigrada = { ...muestra }

    // Migrar fraganciaAromaNotas
    if (muestra.fraganciaAromaNotas) {
      const notas = muestra.fraganciaAromaNotas
      // Si tiene el formato antiguo (categoria como string)
      if ('categoria' in notas && typeof notas.categoria === 'string') {
        muestraMigrada.fraganciaAromaNotas = {
          categorias: notas.categoria ? [notas.categoria] : [],
          subcategorias: notas.subcategoria ? [notas.subcategoria] : [],
          notaEspecifica: notas.notaEspecifica,
        }
      }
      // Si ya tiene el formato nuevo pero es null
      else if (notas.categorias === null || notas.categorias === undefined) {
        muestraMigrada.fraganciaAromaNotas = {
          categorias: [],
          subcategorias: [],
          notaEspecifica: notas.notaEspecifica || null,
        }
      }
    }

    // Migrar saborNotas
    if (muestra.saborNotas) {
      const notas = muestra.saborNotas
      // Si tiene el formato antiguo (categoria como string)
      if ('categoria' in notas && typeof notas.categoria === 'string') {
        muestraMigrada.saborNotas = {
          categorias: notas.categoria ? [notas.categoria] : [],
          subcategorias: notas.subcategoria ? [notas.subcategoria] : [],
          notaEspecifica: notas.notaEspecifica,
        }
      }
      // Si ya tiene el formato nuevo pero es null
      else if (notas.categorias === null || notas.categorias === undefined) {
        muestraMigrada.saborNotas = {
          categorias: [],
          subcategorias: [],
          notaEspecifica: notas.notaEspecifica || null,
        }
      }
    }

    return muestraMigrada
  })

  return {
    ...sesion,
    muestras: muestrasMigradas,
  }
}

/**
 * Carga la sesión activa desde IndexedDB
 */
async function loadSession(): Promise<SesionCatacion | null> {
  try {
    const db = await openDatabase()
    const transaction = db.transaction([STORE_NAME], 'readonly')
    const objectStore = transaction.objectStore(STORE_NAME)

    const sesion = await new Promise<SesionCatacion | null>((resolve, reject) => {
      // Obtener todas las claves
      const getAllRequest = objectStore.getAll()

      getAllRequest.onsuccess = () => {
        const sesiones = getAllRequest.result as SesionCatacion[]
        // Retornar la primera sesión (debería haber solo una)
        let sesion = sesiones.length > 0 ? sesiones[0] : null

        // Migrar sesión si es necesario
        if (sesion) {
          sesion = migrateSesion(sesion)
        }

        resolve(sesion || null)
      }

      getAllRequest.onerror = () => {
        reject(new Error('Error al cargar la sesión'))
      }
    })

    db.close()
    return sesion
  } catch (error) {
    console.error('Error en loadSession:', error)
    return null
  }
}

/**
 * Verifica si existe una sesión activa
 */
async function hasActiveSession(): Promise<boolean> {
  try {
    const sesion = await loadSession()
    return sesion !== null
  } catch (error) {
    console.error('Error en hasActiveSession:', error)
    return false
  }
}

/**
 * Elimina la sesión activa
 */
async function deleteSession(): Promise<void> {
  try {
    const db = await openDatabase()
    const transaction = db.transaction([STORE_NAME], 'readwrite')
    const objectStore = transaction.objectStore(STORE_NAME)

    await new Promise<void>((resolve, reject) => {
      const clearRequest = objectStore.clear()
      clearRequest.onsuccess = () => resolve()
      clearRequest.onerror = () => reject(new Error('Error al eliminar la sesión'))
    })

    db.close()
  } catch (error) {
    console.error('Error en deleteSession:', error)
    throw error
  }
}

/**
 * Actualiza una sesión existente (timestamp de modificación)
 */
async function updateSession(sesion: SesionCatacion): Promise<void> {
  try {
    // Usar toRaw para convertir proxies reactivos de Vue a objetos planos
    // antes de guardar en IndexedDB, pero necesitamos clonar profundamente
    // para evitar referencias a objetos reactivos
    const { toRaw } = await import('vue')
    const sesionPlana = JSON.parse(JSON.stringify(toRaw(sesion))) as SesionCatacion

    // Actualizar timestamp solo en la copia, no en el objeto reactivo
    sesionPlana.modificadoEn = Date.now()

    const db = await openDatabase()
    const transaction = db.transaction([STORE_NAME], 'readwrite')
    const objectStore = transaction.objectStore(STORE_NAME)

    await new Promise<void>((resolve, reject) => {
      const putRequest = objectStore.put(sesionPlana)
      putRequest.onsuccess = () => resolve()
      putRequest.onerror = () => reject(new Error('Error al actualizar la sesión'))
    })

    db.close()
  } catch (error) {
    console.error('Error en updateSession:', error)
    throw error
  }
}

/**
 * Composable principal para manejo de IndexedDB
 */
export const useIndexedDB = () => {
  // Estado reactivo de la sesión
  const sesionActiva = useState<SesionCatacion | null>('sesion-activa', () => null)
  const cargando = useState<boolean>('sesion-cargando', () => false)
  const error = useState<Error | null>('sesion-error', () => null)

  /**
   * Inicializa el composable cargando la sesión activa
   */
  const inicializar = async () => {
    if (import.meta.server) {
      // No hacer nada en el servidor
      return
    }

    try {
      cargando.value = true
      error.value = null
      const sesion = await loadSession()
      sesionActiva.value = sesion
    } catch (err) {
      error.value = err as Error
      console.error('Error al inicializar IndexedDB:', err)
    } finally {
      cargando.value = false
    }
  }

  /**
   * Guarda la sesión activa
   */
  const guardar = async (sesion: SesionCatacion) => {
    if (import.meta.server) {
      console.warn('No se puede guardar en IndexedDB desde el servidor')
      return
    }

    try {
      cargando.value = true
      error.value = null
      await saveSession(sesion)
      sesionActiva.value = sesion
    } catch (err) {
      error.value = err as Error
      console.error('Error al guardar sesión:', err)
      throw err
    } finally {
      cargando.value = false
    }
  }

  /**
   * Actualiza la sesión activa
   */
  const actualizar = async (sesion: SesionCatacion) => {
    if (import.meta.server) {
      console.warn('No se puede actualizar en IndexedDB desde el servidor')
      return
    }

    try {
      // NO activar cargando durante actualizaciones para evitar
      // desmontar los componentes de la UI
      error.value = null
      await updateSession(sesion)
      // NO reemplazar el objeto completo para mantener las referencias de Vue
      // sesionActiva.value = sesion
    } catch (err) {
      error.value = err as Error
      console.error('Error al actualizar sesión:', err)
      throw err
    }
  }

  /**
   * Elimina la sesión activa
   */
  const eliminar = async () => {
    if (import.meta.server) {
      console.warn('No se puede eliminar de IndexedDB desde el servidor')
      return
    }

    try {
      cargando.value = true
      error.value = null
      await deleteSession()
      sesionActiva.value = null
    } catch (err) {
      error.value = err as Error
      console.error('Error al eliminar sesión:', err)
      throw err
    } finally {
      cargando.value = false
    }
  }

  /**
   * Verifica si hay una sesión activa
   */
  const tieneSecion = computed(() => sesionActiva.value !== null)

  return {
    // Estado
    sesionActiva, // No readonly para permitir mutaciones directas
    cargando: readonly(cargando),
    error: readonly(error),
    tieneSecion,

    // Métodos
    inicializar,
    guardar,
    actualizar,
    eliminar,
  }
}
