/**
 * Voice Encryption - Client-Side AES-256-GCM
 * DSGVO-compliant: Encryption key NEVER leaves the device
 *
 * The master key is stored in IndexedDB (encrypted with device key)
 * Server only receives:
 * - Key hash (for verification)
 * - Encrypted blobs
 * - Namespace ID (pseudonym)
 *
 * NOTE: crypto.subtle is only available in secure contexts (HTTPS or localhost).
 * In development over HTTP (e.g., http://macmini:3000), encryption is disabled.
 */

/**
 * Check if we're in a secure context where crypto.subtle is available
 */
export function isSecureContext(): boolean {
  if (typeof window === 'undefined') return false
  if (typeof crypto === 'undefined') return false
  return crypto.subtle !== undefined
}

/**
 * Check if encryption is available
 * Returns false in non-secure HTTP contexts
 */
export function isEncryptionAvailable(): boolean {
  return isSecureContext()
}

const DB_NAME = 'breakpilot-voice'
const DB_VERSION = 1
const STORE_NAME = 'keys'
const MASTER_KEY_ID = 'master-key'

/**
 * Open IndexedDB for key storage
 */
async function openDatabase(): Promise<IDBDatabase> {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(DB_NAME, DB_VERSION)

    request.onerror = () => reject(request.error)
    request.onsuccess = () => resolve(request.result)

    request.onupgradeneeded = (event) => {
      const db = (event.target as IDBOpenDBRequest).result
      if (!db.objectStoreNames.contains(STORE_NAME)) {
        db.createObjectStore(STORE_NAME, { keyPath: 'id' })
      }
    }
  })
}

/**
 * Generate a new master key for encryption
 * This is called once when the teacher first uses voice features
 * Returns null if encryption is not available (non-secure context)
 */
export async function generateMasterKey(): Promise<CryptoKey | null> {
  if (!isEncryptionAvailable()) {
    console.warn('[VoiceEncryption] crypto.subtle nicht verfügbar - HTTP-Kontext erkannt. Verschlüsselung deaktiviert.')
    return null
  }

  const key = await crypto.subtle.generateKey(
    { name: 'AES-GCM', length: 256 },
    true, // extractable for storage
    ['encrypt', 'decrypt']
  )

  // Store in IndexedDB
  await storeMasterKey(key)

  return key
}

/**
 * Store master key in IndexedDB
 */
async function storeMasterKey(key: CryptoKey): Promise<void> {
  if (!isEncryptionAvailable()) return

  const db = await openDatabase()
  const exportedKey = await crypto.subtle.exportKey('raw', key)

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

    const request = store.put({
      id: MASTER_KEY_ID,
      key: Array.from(new Uint8Array(exportedKey)),
      createdAt: new Date().toISOString(),
    })

    request.onerror = () => reject(request.error)
    request.onsuccess = () => resolve()
  })
}

/**
 * Get master key from IndexedDB
 * Returns null if encryption is not available
 */
export async function getMasterKey(): Promise<CryptoKey | null> {
  if (!isEncryptionAvailable()) {
    return 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(MASTER_KEY_ID)

      request.onerror = () => reject(request.error)
      request.onsuccess = async () => {
        const record = request.result
        if (!record) {
          resolve(null)
          return
        }

        // Import key
        const keyData = new Uint8Array(record.key)
        const key = await crypto.subtle.importKey(
          'raw',
          keyData,
          { name: 'AES-GCM', length: 256 },
          true,
          ['encrypt', 'decrypt']
        )
        resolve(key)
      }
    })
  } catch {
    return null
  }
}

/**
 * Generate key hash for server verification
 * Format: "sha256:base64encodedHash"
 * Returns "disabled" if encryption is not available
 */
export async function generateKeyHash(key: CryptoKey | null): Promise<string> {
  if (!key || !isEncryptionAvailable()) {
    return 'disabled'
  }

  const exportedKey = await crypto.subtle.exportKey('raw', key)
  const hashBuffer = await crypto.subtle.digest('SHA-256', exportedKey)
  const hashArray = Array.from(new Uint8Array(hashBuffer))
  const hashBase64 = btoa(String.fromCharCode(...hashArray))
  return `sha256:${hashBase64}`
}

/**
 * Encrypt content before sending to server
 * @param content - Plaintext content
 * @param key - Master key
 * @returns Base64 encoded encrypted content
 */
export async function encryptContent(
  content: string,
  key: CryptoKey
): Promise<string> {
  const iv = crypto.getRandomValues(new Uint8Array(12))
  const encoded = new TextEncoder().encode(content)

  const ciphertext = await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    key,
    encoded
  )

  // Combine IV + ciphertext
  const combined = new Uint8Array(iv.length + ciphertext.byteLength)
  combined.set(iv)
  combined.set(new Uint8Array(ciphertext), iv.length)

  return btoa(String.fromCharCode(...combined))
}

/**
 * Decrypt content received from server
 * @param encrypted - Base64 encoded encrypted content
 * @param key - Master key
 * @returns Decrypted plaintext
 */
export async function decryptContent(
  encrypted: string,
  key: CryptoKey
): Promise<string> {
  const data = Uint8Array.from(atob(encrypted), (c) => c.charCodeAt(0))

  const iv = data.slice(0, 12)
  const ciphertext = data.slice(12)

  const decrypted = await crypto.subtle.decrypt(
    { name: 'AES-GCM', iv },
    key,
    ciphertext
  )

  return new TextDecoder().decode(decrypted)
}

/**
 * Generate a namespace ID for the teacher
 * This is a pseudonym that doesn't contain PII
 */
export function generateNamespaceId(): string {
  const bytes = crypto.getRandomValues(new Uint8Array(16))
  const hex = Array.from(bytes)
    .map((b) => b.toString(16).padStart(2, '0'))
    .join('')
  return `ns-${hex}`
}

/**
 * Get or create namespace ID
 */
export async function getNamespaceId(): Promise<string> {
  const stored = localStorage.getItem('breakpilot-namespace-id')
  if (stored) {
    return stored
  }

  const newId = generateNamespaceId()
  localStorage.setItem('breakpilot-namespace-id', newId)
  return newId
}

/**
 * VoiceEncryption class for managing encryption state
 */
export class VoiceEncryption {
  private masterKey: CryptoKey | null = null
  private keyHash: string | null = null
  private namespaceId: string | null = null
  private encryptionEnabled: boolean = false

  /**
   * Initialize encryption
   * Creates master key if not exists
   * In non-secure contexts (HTTP), encryption is disabled but the class still works
   */
  async initialize(): Promise<void> {
    // Check if encryption is available
    this.encryptionEnabled = isEncryptionAvailable()

    if (!this.encryptionEnabled) {
      console.warn('[VoiceEncryption] Verschlüsselung deaktiviert - kein sicherer Kontext (HTTPS erforderlich)')
      console.warn('[VoiceEncryption] Für Produktion bitte HTTPS verwenden!')
      this.keyHash = 'disabled'
      this.namespaceId = await getNamespaceId()
      return
    }

    // Get or create master key
    this.masterKey = await getMasterKey()
    if (!this.masterKey) {
      this.masterKey = await generateMasterKey()
    }

    // Generate key hash
    this.keyHash = await generateKeyHash(this.masterKey)

    // Get namespace ID
    this.namespaceId = await getNamespaceId()
  }

  /**
   * Check if encryption is initialized
   */
  isInitialized(): boolean {
    // Consider initialized even if encryption is disabled
    return this.keyHash !== null
  }

  /**
   * Check if encryption is actually enabled
   */
  isEncryptionEnabled(): boolean {
    return this.encryptionEnabled && this.masterKey !== null
  }

  /**
   * Get key hash for server authentication
   */
  getKeyHash(): string | null {
    return this.keyHash
  }

  /**
   * Get namespace ID
   */
  getNamespaceId(): string | null {
    return this.namespaceId
  }

  /**
   * Encrypt content
   * Returns plaintext if encryption is disabled
   */
  async encrypt(content: string): Promise<string> {
    if (!this.encryptionEnabled || !this.masterKey) {
      // In development without HTTPS, return content as-is (base64 encoded for consistency)
      return btoa(unescape(encodeURIComponent(content)))
    }
    return encryptContent(content, this.masterKey)
  }

  /**
   * Decrypt content
   * Returns content as-is if encryption is disabled
   */
  async decrypt(encrypted: string): Promise<string> {
    if (!this.encryptionEnabled || !this.masterKey) {
      // In development without HTTPS, decode base64
      try {
        return decodeURIComponent(escape(atob(encrypted)))
      } catch {
        return encrypted
      }
    }
    return decryptContent(encrypted, this.masterKey)
  }
}
