/**
 * Document ID Mapping Utility
 *
 * Manages the mapping between room IDs (human-readable slugs) and
 * Automerge document IDs (automerge:xxxx format).
 *
 * This is necessary because:
 * - Automerge requires specific document ID formats
 * - We want to persist documents in IndexedDB with consistent IDs
 * - Room IDs are user-friendly slugs that may not match Automerge's format
 */

const DB_NAME = 'canvas-document-mappings'
const STORE_NAME = 'mappings'
const DB_VERSION = 1

interface DocumentMapping {
  roomId: string
  documentId: string
  createdAt: number
  lastAccessedAt: number
}

let dbInstance: IDBDatabase | null = null

/**
 * Open the IndexedDB database for document ID mappings
 */
async function openDatabase(): Promise<IDBDatabase> {
  if (dbInstance) return dbInstance

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

    request.onerror = () => {
      console.error('Failed to open document mapping database:', request.error)
      reject(request.error)
    }

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

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

      if (!db.objectStoreNames.contains(STORE_NAME)) {
        const store = db.createObjectStore(STORE_NAME, { keyPath: 'roomId' })
        store.createIndex('documentId', 'documentId', { unique: true })
        store.createIndex('lastAccessedAt', 'lastAccessedAt', { unique: false })
      }
    }
  })
}

/**
 * Get the Automerge document ID for a given room ID
 * Returns null if no mapping exists
 */
export async function getDocumentId(roomId: string): Promise<string | null> {
  try {
    const db = await openDatabase()

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

      request.onerror = () => {
        console.error('Failed to get document mapping:', request.error)
        reject(request.error)
      }

      request.onsuccess = () => {
        const mapping = request.result as DocumentMapping | undefined
        if (mapping) {
          // Update last accessed time in background
          updateLastAccessed(roomId).catch(console.error)
          resolve(mapping.documentId)
        } else {
          resolve(null)
        }
      }
    })
  } catch (error) {
    console.error('Error getting document ID:', error)
    return null
  }
}

/**
 * Save a mapping between room ID and Automerge document ID
 */
export async function saveDocumentId(roomId: string, documentId: string): Promise<void> {
  try {
    const db = await openDatabase()

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

      const mapping: DocumentMapping = {
        roomId,
        documentId,
        createdAt: Date.now(),
        lastAccessedAt: Date.now()
      }

      const request = store.put(mapping)

      request.onerror = () => {
        console.error('Failed to save document mapping:', request.error)
        reject(request.error)
      }

      request.onsuccess = () => {
        resolve()
      }
    })
  } catch (error) {
    console.error('Error saving document ID:', error)
    throw error
  }
}

/**
 * Update the last accessed timestamp for a room
 */
async function updateLastAccessed(roomId: string): Promise<void> {
  try {
    const db = await openDatabase()

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

      getRequest.onerror = () => reject(getRequest.error)

      getRequest.onsuccess = () => {
        const mapping = getRequest.result as DocumentMapping | undefined
        if (mapping) {
          mapping.lastAccessedAt = Date.now()
          store.put(mapping)
        }
        resolve()
      }
    })
  } catch (error) {
    // Silent fail for background update
  }
}

/**
 * Delete a document mapping (useful for cleanup)
 */
export async function deleteDocumentMapping(roomId: string): Promise<void> {
  try {
    const db = await openDatabase()

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

      request.onerror = () => {
        console.error('Failed to delete document mapping:', request.error)
        reject(request.error)
      }

      request.onsuccess = () => {
        resolve()
      }
    })
  } catch (error) {
    console.error('Error deleting document mapping:', error)
    throw error
  }
}

/**
 * Get all document mappings (useful for debugging/management)
 */
export async function getAllMappings(): Promise<DocumentMapping[]> {
  try {
    const db = await openDatabase()

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

      request.onerror = () => {
        console.error('Failed to get all document mappings:', request.error)
        reject(request.error)
      }

      request.onsuccess = () => {
        resolve(request.result as DocumentMapping[])
      }
    })
  } catch (error) {
    console.error('Error getting all mappings:', error)
    return []
  }
}

/**
 * Clean up old document mappings (documents not accessed in X days)
 * This helps manage storage quota
 */
export async function cleanupOldMappings(maxAgeDays: number = 30): Promise<number> {
  try {
    const db = await openDatabase()
    const cutoffTime = Date.now() - (maxAgeDays * 24 * 60 * 60 * 1000)

    return new Promise((resolve, reject) => {
      const transaction = db.transaction(STORE_NAME, 'readwrite')
      const store = transaction.objectStore(STORE_NAME)
      const index = store.index('lastAccessedAt')
      const range = IDBKeyRange.upperBound(cutoffTime)
      const request = index.openCursor(range)

      let deletedCount = 0

      request.onerror = () => {
        console.error('Failed to cleanup old mappings:', request.error)
        reject(request.error)
      }

      request.onsuccess = (event) => {
        const cursor = (event.target as IDBRequest).result
        if (cursor) {
          cursor.delete()
          deletedCount++
          cursor.continue()
        } else {
          resolve(deletedCount)
        }
      }
    })
  } catch (error) {
    console.error('Error cleaning up old mappings:', error)
    return 0
  }
}
