/**
 * EncryptID → DocCrypto Bridge
 *
 * Connects the EncryptID WebAuthn PRF output to the local-first
 * DocCrypto layer for client-side document encryption.
 *
 * Data flow:
 *   WebAuthn PRF output
 *     → DocCrypto.initFromPRF()
 *       → deriveSpaceKey(spaceId)
 *         → deriveDocKey(spaceKey, docId)
 *           → AES-256-GCM encrypt/decrypt
 *
 * Usage:
 *   import { EncryptedDocBridge } from './encryptid-bridge';
 *
 *   const bridge = new EncryptedDocBridge();
 *
 *   // After EncryptID authentication (PRF output available):
 *   await bridge.initFromAuth(authResult.prfOutput);
 *
 *   // Pass to EncryptedDocStore for a space:
 *   const store = new EncryptedDocStore(spaceSlug, bridge.getDocCrypto());
 *
 *   // On sign-out:
 *   bridge.clear();
 */

import { DocCrypto } from './crypto';
import { BackupSyncManager } from './backup';
import type { EncryptedDocStore } from './storage';

// ============================================================================
// TYPES
// ============================================================================

export interface BridgeState {
	initialized: boolean;
	/** Cached space keys for quick doc key derivation */
	spaceKeys: Map<string, CryptoKey>;
}

// ============================================================================
// EncryptedDocBridge
// ============================================================================

export class EncryptedDocBridge {
	#crypto: DocCrypto;
	#spaceKeys = new Map<string, CryptoKey>();
	#initialized = false;

	constructor() {
		this.#crypto = new DocCrypto();
	}

	/**
	 * Initialize from WebAuthn PRF output.
	 * Call this after EncryptID authentication returns prfOutput.
	 */
	async initFromAuth(prfOutput: ArrayBuffer): Promise<void> {
		await this.#crypto.initFromPRF(prfOutput);
		this.#spaceKeys.clear();
		this.#initialized = true;
	}

	/**
	 * Initialize from raw key material (e.g., from EncryptIDKeyManager's
	 * encryption key or any 256-bit key).
	 */
	async initFromKey(key: CryptoKey | Uint8Array | ArrayBuffer): Promise<void> {
		await this.#crypto.init(key);
		this.#spaceKeys.clear();
		this.#initialized = true;
	}

	/**
	 * Get the DocCrypto instance for use with EncryptedDocStore.
	 * Returns null if not initialized.
	 */
	getDocCrypto(): DocCrypto | undefined {
		return this.#initialized ? this.#crypto : undefined;
	}

	/**
	 * Pre-derive and cache a space key for faster doc key derivation.
	 */
	async warmSpaceKey(spaceId: string): Promise<void> {
		if (!this.#initialized) return;
		if (!this.#spaceKeys.has(spaceId)) {
			const key = await this.#crypto.deriveSpaceKey(spaceId);
			this.#spaceKeys.set(spaceId, key);
		}
	}

	/**
	 * Get a cached space key (or derive it).
	 */
	async getSpaceKey(spaceId: string): Promise<CryptoKey> {
		if (!this.#initialized) throw new Error('Bridge not initialized');
		let key = this.#spaceKeys.get(spaceId);
		if (!key) {
			key = await this.#crypto.deriveSpaceKey(spaceId);
			this.#spaceKeys.set(spaceId, key);
		}
		return key;
	}

	/**
	 * Derive a document key directly (convenience for one-off operations).
	 */
	async deriveDocKey(spaceId: string, docId: string): Promise<CryptoKey> {
		const spaceKey = await this.getSpaceKey(spaceId);
		return this.#crypto.deriveDocKey(spaceKey, docId);
	}

	/**
	 * Check if the bridge is ready for encryption operations.
	 */
	get isInitialized(): boolean {
		return this.#initialized;
	}

	/**
	 * Clear all key material from memory. Call on sign-out.
	 */
	clear(): void {
		this.#crypto.clear();
		this.#spaceKeys.clear();
		this.#initialized = false;
	}
}

// ============================================================================
// Singleton for app-wide access
// ============================================================================

let _bridge: EncryptedDocBridge | null = null;

/**
 * Get the global EncryptedDocBridge singleton.
 */
export function getDocBridge(): EncryptedDocBridge {
	if (!_bridge) {
		_bridge = new EncryptedDocBridge();
	}
	return _bridge;
}

/**
 * Reset the global bridge (e.g., on sign-out).
 */
export function resetDocBridge(): void {
	if (_bridge) {
		_bridge.clear();
		_bridge = null;
	}
}

// ============================================================================
// Helper: check if a space supports client-side encryption
// ============================================================================

/**
 * Check localStorage for whether the user has encryption enabled for a space.
 * This is set by the space encryption toggle in the UI.
 */
export function isSpaceEncryptionEnabled(spaceSlug: string): boolean {
	try {
		return localStorage.getItem(`rspace:${spaceSlug}:encrypted`) === 'true';
	} catch {
		return false;
	}
}

/**
 * Set the client-side encryption flag for a space.
 */
export function setSpaceEncryptionEnabled(spaceSlug: string, enabled: boolean): void {
	try {
		if (enabled) {
			localStorage.setItem(`rspace:${spaceSlug}:encrypted`, 'true');
		} else {
			localStorage.removeItem(`rspace:${spaceSlug}:encrypted`);
		}
	} catch {
		// localStorage unavailable (SSR, etc.)
	}
}

/**
 * Check if encrypted backup is enabled globally.
 */
export function isEncryptedBackupEnabled(): boolean {
	try {
		return localStorage.getItem('encryptid_backup_enabled') === 'true';
	} catch {
		return false;
	}
}

/**
 * Toggle encrypted backup flag.
 * When enabled, creates and starts auto-backup for the given store.
 * When disabled, stops auto-backup and destroys the manager.
 */
export function setEncryptedBackupEnabled(
	enabled: boolean,
	store?: EncryptedDocStore,
	spaceId?: string,
): void {
	try {
		if (enabled) {
			localStorage.setItem('encryptid_backup_enabled', 'true');
		} else {
			localStorage.removeItem('encryptid_backup_enabled');
		}
	} catch {
		// localStorage unavailable
	}

	// Wire up BackupSyncManager
	if (enabled && store && spaceId) {
		if (_backupManager) {
			_backupManager.destroy();
		}
		_backupManager = new BackupSyncManager(spaceId, store);
		_backupManager.setAutoBackup(true);
	} else if (!enabled && _backupManager) {
		_backupManager.destroy();
		_backupManager = null;
	}
}

// ============================================================================
// Backup Manager singleton
// ============================================================================

let _backupManager: BackupSyncManager | null = null;

/**
 * Get the current BackupSyncManager (if backup is enabled).
 */
export function getBackupManager(): BackupSyncManager | null {
	return _backupManager;
}

/**
 * Create a BackupSyncManager for the given space.
 * Call this after auth + store setup when backup is enabled.
 */
export function initBackupManager(
	spaceId: string,
	store: EncryptedDocStore,
	baseUrl?: string,
): BackupSyncManager {
	if (_backupManager) {
		_backupManager.destroy();
	}
	_backupManager = new BackupSyncManager(spaceId, store, baseUrl);
	if (isEncryptedBackupEnabled()) {
		_backupManager.setAutoBackup(true);
	}
	return _backupManager;
}
