/**
 * Storage utility for managing localStorage and sessionStorage
 * with type safety and JSON serialization
 */

type StorageType = 'local' | 'session'

interface StorageOptions {
  prefix?: string
  encrypt?: boolean
}

class StorageManager {
  private prefix: string
  private encrypt: boolean

  constructor(options: StorageOptions = {}) {
    this.prefix = options.prefix || 'samms_'
    this.encrypt = options.encrypt || false
  }

  private getStorage(type: StorageType): Storage {
    return type === 'local' ? localStorage : sessionStorage
  }

  private getKey(key: string): string {
    return `${this.prefix}${key}`
  }

  private serialize<T>(value: T): string {
    const serialized = JSON.stringify(value)
    if (this.encrypt) {
      // Simple base64 encoding for obfuscation (not secure encryption)
      return btoa(serialized)
    }
    return serialized
  }

  private deserialize<T>(value: string): T | null {
    try {
      let data = value
      if (this.encrypt) {
        // Simple base64 decoding
        data = atob(value)
      }
      return JSON.parse(data) as T
    } catch {
      return null
    }
  }

  /**
   * Get item from storage
   */
  get<T>(key: string, type: StorageType = 'local'): T | null {
    try {
      const storage = this.getStorage(type)
      const value = storage.getItem(this.getKey(key))
      if (value === null) return null
      return this.deserialize<T>(value)
    } catch {
      return null
    }
  }

  /**
   * Set item in storage
   */
  set<T>(key: string, value: T, type: StorageType = 'local'): boolean {
    try {
      const storage = this.getStorage(type)
      storage.setItem(this.getKey(key), this.serialize(value))
      return true
    } catch {
      return false
    }
  }

  /**
   * Remove item from storage
   */
  remove(key: string, type: StorageType = 'local'): boolean {
    try {
      const storage = this.getStorage(type)
      storage.removeItem(this.getKey(key))
      return true
    } catch {
      return false
    }
  }

  /**
   * Check if item exists in storage
   */
  has(key: string, type: StorageType = 'local'): boolean {
    const storage = this.getStorage(type)
    return storage.getItem(this.getKey(key)) !== null
  }

  /**
   * Clear all items with prefix from storage
   */
  clear(type: StorageType = 'local'): void {
    const storage = this.getStorage(type)
    const keysToRemove: string[] = []

    for (let i = 0; i < storage.length; i++) {
      const key = storage.key(i)
      if (key && key.startsWith(this.prefix)) {
        keysToRemove.push(key)
      }
    }

    keysToRemove.forEach((key) => storage.removeItem(key))
  }

  /**
   * Get all keys with prefix
   */
  keys(type: StorageType = 'local'): string[] {
    const storage = this.getStorage(type)
    const keys: string[] = []

    for (let i = 0; i < storage.length; i++) {
      const key = storage.key(i)
      if (key && key.startsWith(this.prefix)) {
        keys.push(key.replace(this.prefix, ''))
      }
    }

    return keys
  }

  /**
   * Get storage size in bytes
   */
  size(type: StorageType = 'local'): number {
    const storage = this.getStorage(type)
    let size = 0

    for (let i = 0; i < storage.length; i++) {
      const key = storage.key(i)
      if (key && key.startsWith(this.prefix)) {
        const value = storage.getItem(key)
        if (value) {
          size += key.length + value.length
        }
      }
    }

    return size * 2 // UTF-16 encoding
  }

  /**
   * Get remaining storage quota
   */
  getRemainingQuota(type: StorageType = 'local'): number {
    // Most browsers have 5-10MB limit
    const DEFAULT_QUOTA = 5 * 1024 * 1024 // 5MB
    const used = this.size(type)
    return DEFAULT_QUOTA - used
  }

  /**
   * Listen to storage changes
   */
  onChange(
    callback: (key: string, newValue: string | null, oldValue: string | null) => void
  ): () => void {
    const handler = (event: StorageEvent) => {
      if (event.key && event.key.startsWith(this.prefix)) {
        const key = event.key.replace(this.prefix, '')
        callback(key, event.newValue, event.oldValue)
      }
    }

    window.addEventListener('storage', handler)

    return () => {
      window.removeEventListener('storage', handler)
    }
  }
}

// Create singleton instance
export const storage = new StorageManager({ prefix: 'samms_' })

/**
 * Memory storage for sensitive data that shouldn't persist
 */
class MemoryStorage {
  private data: Map<string, unknown> = new Map()

  get<T>(key: string): T | null {
    return (this.data.get(key) as T) || null
  }

  set<T>(key: string, value: T): void {
    this.data.set(key, value)
  }

  remove(key: string): void {
    this.data.delete(key)
  }

  has(key: string): boolean {
    return this.data.has(key)
  }

  clear(): void {
    this.data.clear()
  }

  keys(): string[] {
    return Array.from(this.data.keys())
  }
}

export const memoryStorage = new MemoryStorage()

/**
 * Cache with TTL (Time To Live)
 */
class CacheStorage {
  private cache: Map<string, { value: unknown; expires: number }> = new Map()

  set<T>(key: string, value: T, ttlSeconds: number): void {
    const expires = Date.now() + ttlSeconds * 1000
    this.cache.set(key, { value, expires })
  }

  get<T>(key: string): T | null {
    const item = this.cache.get(key)
    if (!item) return null

    if (Date.now() > item.expires) {
      this.cache.delete(key)
      return null
    }

    return item.value as T
  }

  has(key: string): boolean {
    const item = this.cache.get(key)
    if (!item) return false

    if (Date.now() > item.expires) {
      this.cache.delete(key)
      return false
    }

    return true
  }

  remove(key: string): void {
    this.cache.delete(key)
  }

  clear(): void {
    this.cache.clear()
  }

  // Clean expired entries
  cleanup(): void {
    const now = Date.now()
    for (const [key, item] of this.cache.entries()) {
      if (now > item.expires) {
        this.cache.delete(key)
      }
    }
  }
}

export const cacheStorage = new CacheStorage()

// Cleanup expired cache entries every minute
if (typeof window !== 'undefined') {
  setInterval(() => {
    cacheStorage.cleanup()
  }, 60000)
}