/**
 * IndexedDB Recording Persistence
 * Handles saving recording chunks to IndexedDB for crash recovery
 */

const DB_NAME = 'SpeakrRecordings';
const DB_VERSION = 1;
const STORE_NAME = 'activeRecording';

let dbInstance = null;

/**
 * Helper to promisify IDBRequest
 */
const promisifyRequest = (request) => {
    return new Promise((resolve, reject) => {
        request.onsuccess = () => resolve(request.result);
        request.onerror = () => reject(request.error);
    });
};

/**
 * Initialize IndexedDB
 */
export const initDB = () => {
    return new Promise((resolve, reject) => {
        if (dbInstance) {
            resolve(dbInstance);
            return;
        }

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

        request.onerror = () => {
            console.error('[RecordingDB] Failed to open database:', request.error);
            reject(request.error);
        };

        request.onsuccess = () => {
            dbInstance = request.result;
            console.log('[RecordingDB] Database opened successfully');
            resolve(dbInstance);
        };

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

            // Create object store for active recording
            if (!db.objectStoreNames.contains(STORE_NAME)) {
                const objectStore = db.createObjectStore(STORE_NAME, { keyPath: 'id' });
                objectStore.createIndex('timestamp', 'timestamp', { unique: false });
                console.log('[RecordingDB] Object store created');
            }
        };
    });
};

/**
 * Save recording metadata and initialize session
 */
export const startRecordingSession = async (recordingData) => {
    try {
        const db = await initDB();
        const transaction = db.transaction([STORE_NAME], 'readwrite');
        const objectStore = transaction.objectStore(STORE_NAME);

        const session = {
            id: 'current',
            timestamp: Date.now(),
            startTime: new Date().toISOString(),
            mode: recordingData.mode,
            notes: recordingData.notes || '',
            tags: recordingData.tags || [],
            asrOptions: recordingData.asrOptions || {},
            chunks: [],
            mimeType: recordingData.mimeType || 'audio/webm',
            duration: 0
        };

        await promisifyRequest(objectStore.put(session));
        console.log('[RecordingDB] Recording session started:', session.id);
        return session;
    } catch (error) {
        console.error('[RecordingDB] Failed to start session:', error);
        throw error;
    }
};

/**
 * Save a recording chunk to IndexedDB
 */
export const saveChunk = async (chunkBlob, chunkIndex) => {
    try {
        // Do async prep work BEFORE creating transaction to avoid auto-close
        const db = await initDB();
        const arrayBuffer = await chunkBlob.arrayBuffer();

        // Now create transaction and do all DB operations quickly
        const transaction = db.transaction([STORE_NAME], 'readwrite');
        const objectStore = transaction.objectStore(STORE_NAME);

        // Get current session
        const session = await promisifyRequest(objectStore.get('current'));

        if (!session) {
            console.warn('[RecordingDB] No active session found');
            return;
        }

        // Add chunk to session
        session.chunks.push({
            index: chunkIndex,
            data: arrayBuffer,
            size: chunkBlob.size,
            timestamp: Date.now()
        });

        // Update session - must happen before transaction auto-closes
        await promisifyRequest(objectStore.put(session));
        // Chunk saved silently to avoid spam (happens every 5 seconds)
    } catch (error) {
        console.error('[RecordingDB] Failed to save chunk:', error);
        // Don't throw - recording should continue even if persistence fails
    }
};

/**
 * Update recording metadata (notes, duration, etc.)
 */
export const updateRecordingMetadata = async (updates) => {
    try {
        const db = await initDB();
        const transaction = db.transaction([STORE_NAME], 'readwrite');
        const objectStore = transaction.objectStore(STORE_NAME);

        const session = await promisifyRequest(objectStore.get('current'));

        if (!session) {
            console.warn('[RecordingDB] No active session to update');
            return;
        }

        // Merge updates
        Object.assign(session, updates);
        await promisifyRequest(objectStore.put(session));
        // Metadata updated silently to avoid spam (happens every 5 seconds)
    } catch (error) {
        console.error('[RecordingDB] Failed to update metadata:', error);
    }
};

/**
 * Check if there's a recoverable recording
 */
export const checkForRecoverableRecording = async () => {
    try {
        const db = await initDB();
        const transaction = db.transaction([STORE_NAME], 'readonly');
        const objectStore = transaction.objectStore(STORE_NAME);

        const session = await promisifyRequest(objectStore.get('current'));

        if (!session || !session.chunks || session.chunks.length === 0) {
            return null;
        }

        // Calculate total size
        const totalSize = session.chunks.reduce((sum, chunk) => sum + chunk.size, 0);

        // Calculate approximate duration (1 second chunks)
        const duration = session.chunks.length;

        console.log('[RecordingDB] Found recoverable recording:', {
            chunks: session.chunks.length,
            size: totalSize,
            duration: duration,
            startTime: session.startTime
        });

        return {
            ...session,
            totalSize,
            duration: duration
        };
    } catch (error) {
        console.error('[RecordingDB] Failed to check for recoverable recording:', error);
        return null;
    }
};

/**
 * Recover recording from IndexedDB
 */
export const recoverRecording = async () => {
    try {
        const db = await initDB();
        const transaction = db.transaction([STORE_NAME], 'readonly');
        const objectStore = transaction.objectStore(STORE_NAME);

        const session = await promisifyRequest(objectStore.get('current'));

        if (!session || !session.chunks || session.chunks.length === 0) {
            console.warn('[RecordingDB] No recording to recover');
            return null;
        }

        // Convert chunks back to Blobs
        const chunks = session.chunks.map(chunk => {
            return new Blob([chunk.data], { type: session.mimeType });
        });

        console.log(`[RecordingDB] Recovered ${chunks.length} chunks`);

        return {
            chunks,
            metadata: {
                mode: session.mode,
                notes: session.notes,
                tags: session.tags,
                asrOptions: session.asrOptions,
                mimeType: session.mimeType,
                duration: session.chunks.length,
                startTime: session.startTime
            }
        };
    } catch (error) {
        console.error('[RecordingDB] Failed to recover recording:', error);
        return null;
    }
};

/**
 * Clear recording session (after successful upload or discard)
 */
export const clearRecordingSession = async () => {
    try {
        const db = await initDB();
        const transaction = db.transaction([STORE_NAME], 'readwrite');
        const objectStore = transaction.objectStore(STORE_NAME);

        await promisifyRequest(objectStore.delete('current'));
        console.log('[RecordingDB] Recording session cleared');
    } catch (error) {
        console.error('[RecordingDB] Failed to clear session:', error);
    }
};

/**
 * Get database size information
 */
export const getDatabaseSize = async () => {
    try {
        if (!navigator.storage || !navigator.storage.estimate) {
            return null;
        }

        const estimate = await navigator.storage.estimate();
        return {
            usage: estimate.usage,
            quota: estimate.quota,
            percentage: ((estimate.usage / estimate.quota) * 100).toFixed(2)
        };
    } catch (error) {
        console.error('[RecordingDB] Failed to get database size:', error);
        return null;
    }
};
