/**
 * Upload management composable
 * Handles file uploads, queue processing, and progress tracking
 */

import * as FailedUploads from '../db/failed-uploads.js';
import * as IncognitoStorage from '../db/incognito-storage.js';

// Parse error message and return friendly error info
function getFriendlyError(errorMessage, t) {
    const _t = t || ((key) => key);
    if (!errorMessage) return { title: _t('errors.processingError'), message: _t('errors.processingErrorMessage') };
    const lowerText = errorMessage.toLowerCase();
    const patterns = [
        { patterns: ['maximum content size limit', 'file too large', '413', 'payload too large', 'exceeded'], title: _t('errors.fileTooLargeTitle'), guidance: _t('errors.enableChunkingGuidance') },
        { patterns: ['timed out', 'timeout', 'deadline exceeded'], title: _t('errors.processingTimeout'), guidance: _t('errors.splitAudioGuidance') },
        { patterns: ['401', 'unauthorized', 'invalid api key', 'authentication failed', 'incorrect api key'], title: _t('errors.authenticationError'), guidance: _t('errors.checkApiKeyGuidance') },
        { patterns: ['rate limit', 'too many requests', '429', 'quota exceeded'], title: _t('errors.rateLimitExceeded'), guidance: _t('errors.waitAndRetryGuidance') },
        { patterns: ['connection refused', 'connection reset', 'could not connect', 'network unreachable'], title: _t('errors.connectionError'), guidance: _t('errors.checkNetworkGuidance') },
        { patterns: ['503', '502', '500', 'service unavailable', 'server error', 'internal server error'], title: _t('errors.serviceUnavailable'), guidance: _t('errors.tryAgainLaterGuidance') },
        { patterns: ['invalid file format', 'unsupported format', 'could not decode', 'corrupt'], title: _t('errors.invalidAudioFormat'), guidance: _t('errors.convertFormatGuidance') },
        { patterns: ['audio extraction failed', 'ffmpeg failed', 'no audio stream'], title: _t('errors.audioExtractionFailed'), guidance: _t('errors.convertStandardGuidance') },
    ];
    for (const pattern of patterns) {
        for (const p of pattern.patterns) {
            if (lowerText.includes(p)) return { title: pattern.title, guidance: pattern.guidance };
        }
    }
    return { title: _t('errors.processingError'), guidance: _t('errors.processingErrorFallbackGuidance') };
}

export function useUpload(state, utils) {
    const {
        uploadQueue, currentlyProcessingFile, processingProgress, processingMessage,
        isProcessingActive, pollInterval, progressPopupMinimized, progressPopupClosed,
        maxFileSizeMB, chunkingEnabled, chunkingMode, chunkingLimit, maxConcurrentUploads,
        recordings, selectedRecording, totalRecordings, globalError,
        selectedTagIds, uploadLanguage, uploadMinSpeakers, uploadMaxSpeakers, uploadHotwords, uploadInitialPrompt,
        useAsrEndpoint, connectorSupportsDiarization, asrLanguage, asrMinSpeakers, asrMaxSpeakers,
        dragover, availableTags, uploadTagSearchFilter,
        // Folder state
        availableFolders, selectedFolderId,
        // Incognito mode state
        incognitoMode, incognitoRecording, incognitoProcessing,
        // View state
        currentView,
        // Upload disclaimer state
        uploadDisclaimer, showUploadDisclaimerModal
    } = state;

    const { computed, nextTick, ref } = Vue;

    const { setGlobalError, showToast, formatFileSize, onChatComplete, t } = utils;

    // Compute selected tags from IDs
    const selectedTags = computed(() => {
        return selectedTagIds.value.map(id =>
            availableTags.value.find(t => t.id === id)
        ).filter(Boolean);
    });

    // --- Tag Drag-and-Drop State ---
    const draggedTagIndex = ref(null);
    const dragOverTagIndex = ref(null);

    // Reorder selectedTagIds array
    const reorderSelectedTags = (fromIndex, toIndex) => {
        const tagIds = [...selectedTagIds.value];
        const [removed] = tagIds.splice(fromIndex, 1);
        tagIds.splice(toIndex, 0, removed);
        selectedTagIds.value = tagIds;
        applyTagDefaults(); // Re-apply defaults since first tag may have changed
    };

    // === MOUSE DRAG HANDLERS ===
    const handleTagDragStart = (index, event) => {
        draggedTagIndex.value = index;
        event.dataTransfer.effectAllowed = 'move';
        event.dataTransfer.setData('text/plain', index.toString());
    };

    const handleTagDragOver = (index, event) => {
        event.preventDefault();
        event.dataTransfer.dropEffect = 'move';
        dragOverTagIndex.value = index;
    };

    const handleTagDrop = (targetIndex, event) => {
        event.preventDefault();
        if (draggedTagIndex.value !== null && draggedTagIndex.value !== targetIndex) {
            reorderSelectedTags(draggedTagIndex.value, targetIndex);
        }
        draggedTagIndex.value = null;
        dragOverTagIndex.value = null;
    };

    const handleTagDragEnd = () => {
        draggedTagIndex.value = null;
        dragOverTagIndex.value = null;
    };

    // === TOUCH HANDLERS (Mobile) ===
    let touchStartIndex = null;

    const handleTagTouchStart = (index, event) => {
        touchStartIndex = index;
        draggedTagIndex.value = index;
    };

    const handleTagTouchMove = (event) => {
        if (touchStartIndex === null) return;
        event.preventDefault();

        const touch = event.touches[0];
        const elementBelow = document.elementFromPoint(touch.clientX, touch.clientY);
        const tagElement = elementBelow?.closest('[data-tag-index]');

        if (tagElement) {
            const targetIndex = parseInt(tagElement.dataset.tagIndex);
            dragOverTagIndex.value = targetIndex;
        }
    };

    const handleTagTouchEnd = () => {
        if (touchStartIndex !== null && dragOverTagIndex.value !== null &&
            touchStartIndex !== dragOverTagIndex.value) {
            reorderSelectedTags(touchStartIndex, dragOverTagIndex.value);
        }
        touchStartIndex = null;
        draggedTagIndex.value = null;
        dragOverTagIndex.value = null;
    };

    // Handle drag events
    const handleDragOver = (e) => {
        e.preventDefault();
        dragover.value = true;
    };

    const handleDragLeave = (e) => {
        if (e.relatedTarget && e.currentTarget.contains(e.relatedTarget)) {
            return;
        }
        dragover.value = false;
    };

    const handleDrop = (e) => {
        e.preventDefault();
        dragover.value = false;
        addFilesToQueue(e.dataTransfer.files);
    };

    const handleFileSelect = (e) => {
        addFilesToQueue(e.target.files);
        e.target.value = null;
    };

    // Add files to the upload queue
    const addFilesToQueue = (files) => {
        let filesAdded = 0;
        for (const file of files) {
            const fileObject = file.file ? file.file : file;
            const notes = file.notes || null;
            const tags = file.tags || selectedTags.value || [];
            const asrOptions = file.asrOptions || {
                language: asrLanguage.value,
                min_speakers: asrMinSpeakers.value,
                max_speakers: asrMaxSpeakers.value
            };

            // Check if it's an audio file or video container with audio
            const isAudioFile = fileObject && (
                fileObject.type.startsWith('audio/') ||
                fileObject.type === 'video/mp4' ||
                fileObject.type === 'video/quicktime' ||
                fileObject.type === 'video/x-msvideo' ||
                fileObject.type === 'video/webm' ||
                fileObject.name.toLowerCase().endsWith('.amr') ||
                fileObject.name.toLowerCase().endsWith('.3gp') ||
                fileObject.name.toLowerCase().endsWith('.3gpp') ||
                fileObject.name.toLowerCase().endsWith('.mp4') ||
                fileObject.name.toLowerCase().endsWith('.mov') ||
                fileObject.name.toLowerCase().endsWith('.avi') ||
                fileObject.name.toLowerCase().endsWith('.mkv') ||
                fileObject.name.toLowerCase().endsWith('.webm') ||
                fileObject.name.toLowerCase().endsWith('.weba')
            );

            if (isAudioFile) {
                // Only check general file size limit
                if (fileObject.size > maxFileSizeMB.value * 1024 * 1024) {
                    setGlobalError(t('upload.fileExceedsMaxSize', { name: fileObject.name, size: maxFileSizeMB.value }));
                    continue;
                }

                const clientId = `client-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;

                uploadQueue.value.push({
                    file: fileObject,
                    notes: notes,
                    tags: tags,
                    asrOptions: asrOptions,
                    status: 'queued',
                    recordingId: null,
                    clientId: clientId,
                    error: null,
                    willAutoSummarize: false // Server will tell us via SUMMARIZING status
                });
                filesAdded++;
            } else if (fileObject) {
                setGlobalError(t('upload.invalidFileType', { name: fileObject.name }));
            }
        }
        if (filesAdded > 0) {
            console.log(`Added ${filesAdded} file(s) to the queue.`);
        }
    };

    // Remove a file from the queue before processing starts
    const removeFromQueue = (clientId) => {
        const index = uploadQueue.value.findIndex(item => item.clientId === clientId);
        if (index !== -1 && (uploadQueue.value[index].status === 'queued' || uploadQueue.value[index].status === 'ready')) {
            uploadQueue.value.splice(index, 1);
            console.log(`Removed file from queue: ${clientId}`);
        }
    };

    // Cancel a waiting file from the upload progress queue
    const cancelWaitingFile = (clientId) => {
        const index = uploadQueue.value.findIndex(item => item.clientId === clientId);
        if (index !== -1 && uploadQueue.value[index].status === 'ready') {
            uploadQueue.value.splice(index, 1);
            console.log(`Cancelled waiting file: ${clientId}`);
            showToast(t('upload.fileRemovedFromQueue'), 'fa-trash');
        }
    };

    // Clear completed uploads from queue
    const clearCompletedUploads = () => {
        uploadQueue.value = uploadQueue.value.filter(item => !['completed', 'failed'].includes(item.status));
    };

    // Start processing all queued files
    const startUpload = () => {
        const pendingFiles = uploadQueue.value.filter(item => item.status === 'queued');
        if (pendingFiles.length === 0) {
            return;
        }
        // Show upload disclaimer if configured
        if (uploadDisclaimer.value && uploadDisclaimer.value.trim() !== '') {
            showUploadDisclaimerModal.value = true;
            return;
        }
        // Update all queued files with current tags and ASR options
        // AND change their status to 'ready' so they move to upload progress immediately
        for (const item of uploadQueue.value) {
            if (item.status === 'queued') {
                if (!item.preserveOptions) {
                    // For file uploads: use current UI selection (user may have changed tags after dropping)
                    item.tags = [...selectedTags.value];
                    item.asrOptions = {
                        language: asrLanguage.value,
                        min_speakers: asrMinSpeakers.value,
                        max_speakers: asrMaxSpeakers.value,
                        hotwords: uploadHotwords.value,
                        initial_prompt: uploadInitialPrompt.value,
                    };
                    item.folder_id = selectedFolderId.value;
                }
                // Change status to 'ready' to remove from upload view but keep in queue
                item.status = 'ready';
            }
        }
        progressPopupMinimized.value = false;
        progressPopupClosed.value = false;
        startProcessingQueue();
    };

    // --- Parallel Upload System ---
    // Concurrency limiter: configurable via MAX_CONCURRENT_UPLOADS env var (default 3)
    let activeUploadCount = 0;
    const pendingUploadQueue = []; // Functions waiting for a slot

    const acquireUploadSlot = () => {
        return new Promise(resolve => {
            if (activeUploadCount < (maxConcurrentUploads?.value || 3)) {
                activeUploadCount++;
                resolve();
            } else {
                pendingUploadQueue.push(resolve);
            }
        });
    };

    const releaseUploadSlot = () => {
        activeUploadCount--;
        if (pendingUploadQueue.length > 0) {
            activeUploadCount++;
            const next = pendingUploadQueue.shift();
            next();
        }
        // When all uploads are done, clear processing active flag
        const stillUploading = uploadQueue.value.some(item =>
            ['uploading', 'ready'].includes(item.status)
        );
        if (!stillUploading) {
            isProcessingActive.value = false;
        }
    };

    const resetCurrentFileProcessingState = () => {
        if (pollInterval.value) clearInterval(pollInterval.value);
        pollInterval.value = null;
        currentlyProcessingFile.value = null;
        processingProgress.value = 0;
        processingMessage.value = '';
    };

    /**
     * Upload a single file to the server.
     * Acquires a concurrency slot, uploads, then releases.
     * Status updates are per-item (no global processingProgress).
     */
    const uploadSingleFile = async (fileItem) => {
        await acquireUploadSlot();

        fileItem.status = 'uploading';
        fileItem.progress = 5;

        try {
            const formData = new FormData();
            formData.append('file', fileItem.file);

            // Send file's lastModified timestamp for meeting_date
            if (fileItem.file.lastModified) {
                const lastModified = fileItem.file.lastModified;
                formData.append('file_last_modified', lastModified.toString());
            }

            if (fileItem.notes) {
                formData.append('notes', fileItem.notes);
            }

            // Add tags if selected
            const tagsToUse = fileItem.tags || selectedTags.value || [];
            tagsToUse.forEach((tag, index) => {
                const tagId = tag.id || tag;
                formData.append(`tag_ids[${index}]`, tagId);
            });

            // Add folder if selected
            const folderToUse = fileItem.folder_id || selectedFolderId.value;
            if (folderToUse) {
                formData.append('folder_id', folderToUse);
            }

            // Add ASR options
            const asrOpts = fileItem.asrOptions || {};
            const language = asrOpts.language || uploadLanguage.value;
            if (language) {
                formData.append('language', language);
            }

            if (connectorSupportsDiarization.value) {
                const minSpeakers = asrOpts.min_speakers || uploadMinSpeakers.value;
                const maxSpeakers = asrOpts.max_speakers || uploadMaxSpeakers.value;

                if (minSpeakers && minSpeakers !== '') {
                    formData.append('min_speakers', minSpeakers.toString());
                }
                if (maxSpeakers && maxSpeakers !== '') {
                    formData.append('max_speakers', maxSpeakers.toString());
                }
            }

            // Add hotwords and initial prompt
            const hotwords = asrOpts.hotwords || uploadHotwords.value;
            const initialPrompt = asrOpts.initial_prompt || uploadInitialPrompt.value;
            if (hotwords && hotwords.trim()) {
                formData.append('hotwords', hotwords.trim());
            }
            if (initialPrompt && initialPrompt.trim()) {
                formData.append('initial_prompt', initialPrompt.trim());
            }

            // Refresh CSRF token before upload (prevents stale token after sleep/idle)
            let csrfToken;
            if (window.csrfManager) {
                try {
                    csrfToken = await window.csrfManager.refreshToken();
                } catch (e) {
                    csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
                }
            } else {
                csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
            }

            // Use XMLHttpRequest for per-file upload progress
            const data = await new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();

                xhr.upload.onprogress = (e) => {
                    if (e.lengthComputable) {
                        // Map upload progress to 5-90% range
                        fileItem.progress = Math.round(5 + (e.loaded / e.total) * 85);
                    }
                };

                xhr.onload = () => {
                    const contentType = xhr.getResponseHeader('content-type') || '';
                    if (!contentType.includes('application/json')) {
                        const titleMatch = xhr.responseText.match(/<title>([^<]+)<\/title>/i);
                        const h1Match = xhr.responseText.match(/<h1>([^<]+)<\/h1>/i);
                        reject(new Error(titleMatch?.[1] || h1Match?.[1] ||
                            `Server error (${xhr.status}): Response was not JSON`));
                        return;
                    }

                    let parsed;
                    try {
                        parsed = JSON.parse(xhr.responseText);
                    } catch {
                        reject(new Error(`Invalid JSON response (${xhr.status})`));
                        return;
                    }

                    if (xhr.status === 202 && parsed.id) {
                        resolve(parsed);
                    } else if (!String(xhr.status).startsWith('2')) {
                        let errorMsg = parsed.error || `Upload failed with status ${xhr.status}`;
                        if (xhr.status === 413) errorMsg = parsed.error || `File too large. Max: ${parsed.max_size_mb?.toFixed(0) || maxFileSizeMB.value} MB.`;
                        reject(new Error(errorMsg));
                    } else {
                        reject(new Error('Unexpected success response from server after upload.'));
                    }
                };

                xhr.onerror = () => reject(new Error('Network error during upload'));
                xhr.ontimeout = () => reject(new Error('Upload timed out'));

                // Store abort controller on item for cancellation
                fileItem._xhr = xhr;

                xhr.open('POST', '/upload');
                if (csrfToken) {
                    xhr.setRequestHeader('X-CSRFToken', csrfToken);
                }
                xhr.send(formData);
            });

            // Upload succeeded - recording is now on the server
            console.log(`File ${fileItem.file.name} uploaded. Recording ID: ${data.id}. Server will process via job queue.`);
            fileItem.status = 'pending';
            fileItem.recordingId = data.id;
            fileItem.progress = 100;

            // Add to recordings list
            recordings.value.unshift(data);
            totalRecordings.value++;

            // Clear recording session only after confirmed upload
            if (fileItem.onUploadSuccess) {
                await fileItem.onUploadSuccess();
            }

            // Handle duplicate warning
            if (data.duplicate_warning) {
                const warning = data.duplicate_warning;
                const existingDate = warning.existing_created_at
                    ? new Date(warning.existing_created_at).toLocaleDateString()
                    : '';
                const existingName = warning.existing_title || 'Unknown';
                showToast(
                    `⚠️ ${existingName} (${existingDate})`,
                    'fa-copy'
                );
                fileItem.duplicateWarning = warning;
            }

        } catch (error) {
            console.error(`Upload Error for ${fileItem.file.name} (Client ID: ${fileItem.clientId}):`, error);
            fileItem.status = 'failed';
            fileItem.error = error.message;
            fileItem.progress = 0;

            // Show friendly error message
            const friendlyErr = getFriendlyError(error.message, t);
            setGlobalError(`${friendlyErr.title}: ${friendlyErr.guidance}`);

            // Store failed upload in IndexedDB for background sync retry
            try {
                await FailedUploads.storeFailedUpload({
                    file: fileItem.file,
                    fileName: fileItem.file.name,
                    fileSize: fileItem.file.size,
                    clientId: fileItem.clientId,
                    notes: fileItem.notes,
                    tags: fileItem.tags,
                    asrOptions: fileItem.asrOptions,
                    error: error.message
                });

                if ('serviceWorker' in navigator && 'sync' in ServiceWorkerRegistration.prototype) {
                    const registration = await navigator.serviceWorker.ready;
                    await registration.sync.register('sync-uploads');
                    console.log('[Upload] Registered background sync for failed upload');
                }
            } catch (syncError) {
                console.warn('[Upload] Failed to register background sync:', syncError);
            }
        } finally {
            fileItem._xhr = null;
            releaseUploadSlot();
        }
    };

    /**
     * Start uploading all ready files in parallel (with concurrency limit).
     * Processing status is tracked via allJobs polling in app.modular.js.
     */
    const startProcessingQueue = async () => {
        const readyItems = uploadQueue.value.filter(item => item.status === 'ready');
        if (readyItems.length === 0) {
            console.log("No files ready to upload.");
            return;
        }

        isProcessingActive.value = true;
        console.log(`Starting parallel upload of ${readyItems.length} file(s) (max ${maxConcurrentUploads?.value || 3} concurrent)...`);

        // Fire off all uploads concurrently (semaphore handles limiting)
        const uploadPromises = readyItems.map(item => uploadSingleFile(item));
        // Don't await - let them run in background. isProcessingActive is cleared by releaseUploadSlot.
        Promise.allSettled(uploadPromises).then(() => {
            console.log('All uploads settled.');
        });
    };

    // Keep backward-compat aliases
    const startStatusPolling = (fileItem, recordingId) => {
        // No longer needed - allJobs polling handles status tracking
        fileItem.recordingId = recordingId;
    };

    const pollProcessingStatus = () => {
        // No-op: status tracking is now handled by allJobs polling in app.modular.js
    };

    // Tag selection helpers
    const addTagToSelection = (tagId) => {
        if (!selectedTagIds.value.includes(tagId)) {
            selectedTagIds.value.push(tagId);
            applyTagDefaults();
        }
    };

    const removeTagFromSelection = (tagId) => {
        const index = selectedTagIds.value.indexOf(tagId);
        if (index > -1) {
            selectedTagIds.value.splice(index, 1);
            applyTagDefaults();
        }
    };

    const applyTagDefaults = () => {
        const selectedTagsObjects = selectedTagIds.value.map(tagId =>
            availableTags.value.find(tag => tag.id == tagId)
        ).filter(Boolean);

        const firstTag = selectedTagsObjects[0];
        if (firstTag && connectorSupportsDiarization.value) {
            if (firstTag.default_language) {
                uploadLanguage.value = firstTag.default_language;
            }
            if (firstTag.default_min_speakers) {
                uploadMinSpeakers.value = firstTag.default_min_speakers;
            }
            if (firstTag.default_max_speakers) {
                uploadMaxSpeakers.value = firstTag.default_max_speakers;
            }
        }
        // Apply hotwords/initial_prompt from first tag (works for all connectors)
        if (firstTag) {
            if (firstTag.default_hotwords) {
                uploadHotwords.value = firstTag.default_hotwords;
            }
            if (firstTag.default_initial_prompt) {
                uploadInitialPrompt.value = firstTag.default_initial_prompt;
            }
        }
    };

    // Computed property for filtered available tags in upload view
    const filteredAvailableTagsForUpload = computed(() => {
        const availableForSelection = availableTags.value.filter(tag => !selectedTagIds.value.includes(tag.id));
        if (!uploadTagSearchFilter.value) return availableForSelection;

        const filter = uploadTagSearchFilter.value.toLowerCase();
        return availableForSelection.filter(tag =>
            tag.name.toLowerCase().includes(filter)
        );
    });

    // === INCOGNITO MODE FUNCTIONS ===

    /**
     * Upload and process a file in incognito mode.
     * The file is processed synchronously and no data is saved to the database.
     * Results are stored only in sessionStorage.
     */
    const startIncognitoUpload = async () => {
        const pendingFiles = uploadQueue.value.filter(item => item.status === 'queued');
        if (pendingFiles.length === 0) {
            return;
        }

        // Only process the first file for incognito mode
        const fileItem = pendingFiles[0];

        // Check if incognito mode state is available
        if (!incognitoMode || !incognitoProcessing || !incognitoRecording) {
            console.warn('[Incognito] Incognito state not available, falling back to normal upload');
            startUpload();
            return;
        }

        incognitoProcessing.value = true;
        processingMessage.value = t('incognito.processingInProgress');
        processingProgress.value = 10;
        progressPopupMinimized.value = false;
        progressPopupClosed.value = false;

        try {
            const formData = new FormData();
            formData.append('file', fileItem.file);

            // Add ASR options
            const asrOpts = fileItem.asrOptions || {};
            const language = asrOpts.language || uploadLanguage.value;
            const minSpeakers = asrOpts.min_speakers || uploadMinSpeakers.value;
            const maxSpeakers = asrOpts.max_speakers || uploadMaxSpeakers.value;

            if (language) {
                formData.append('language', language);
            }
            if (minSpeakers && minSpeakers !== '') {
                formData.append('min_speakers', minSpeakers.toString());
            }
            if (maxSpeakers && maxSpeakers !== '') {
                formData.append('max_speakers', maxSpeakers.toString());
            }

            const hotwords = asrOpts.hotwords || uploadHotwords.value;
            const initialPrompt = asrOpts.initial_prompt || uploadInitialPrompt.value;
            if (hotwords && hotwords.trim()) {
                formData.append('hotwords', hotwords.trim());
            }
            if (initialPrompt && initialPrompt.trim()) {
                formData.append('initial_prompt', initialPrompt.trim());
            }

            // Request auto-summarization
            formData.append('auto_summarize', 'true');

            processingMessage.value = t('incognito.uploadingFile');
            processingProgress.value = 20;

            console.log('[Incognito] Uploading file:', fileItem.file.name);

            const response = await fetch('/api/recordings/incognito', {
                method: 'POST',
                body: formData
            });

            processingProgress.value = 50;

            // Parse response
            const contentType = response.headers.get('content-type') || '';
            if (!contentType.includes('application/json')) {
                const text = await response.text();
                const titleMatch = text.match(/<title>([^<]+)<\/title>/i);
                throw new Error(titleMatch?.[1] || `Server error (${response.status})`);
            }

            const data = await response.json();

            if (!response.ok || data.error) {
                throw new Error(data.error || `Processing failed with status ${response.status}`);
            }

            processingProgress.value = 80;
            processingMessage.value = t('incognito.processingComplete');

            // Store result in sessionStorage
            const incognitoData = {
                id: 'incognito',
                incognito: true,
                title: data.title || t('incognito.recordingTitle'),
                transcription: data.transcription,
                summary: data.summary,
                summary_html: data.summary_html,
                created_at: data.created_at,
                original_filename: data.original_filename,
                file_size: data.file_size,
                audio_duration_seconds: data.audio_duration_seconds,
                processing_time_seconds: data.processing_time_seconds,
                status: 'COMPLETED'
            };

            IncognitoStorage.saveIncognitoRecording(incognitoData);
            incognitoRecording.value = incognitoData;

            // Remove the processed file from queue
            const index = uploadQueue.value.findIndex(item => item.clientId === fileItem.clientId);
            if (index !== -1) {
                uploadQueue.value.splice(index, 1);
            }

            processingProgress.value = 100;
            processingMessage.value = t('incognito.recordingReady');

            // Auto-select the incognito recording and switch to detail view
            selectedRecording.value = incognitoData;
            currentView.value = 'detail';

            // Show toast
            showToast(t('incognito.recordingProcessed'), 'fa-user-secret');

            console.log('[Incognito] Processing complete');

        } catch (error) {
            console.error('[Incognito] Processing failed:', error);
            const friendlyErr = getFriendlyError(error.message, t);
            setGlobalError(`${friendlyErr.title}: ${friendlyErr.guidance}`);
            fileItem.status = 'failed';
            fileItem.error = error.message;
        } finally {
            incognitoProcessing.value = false;
            processingProgress.value = 0;
            processingMessage.value = '';
        }
    };

    /**
     * Clear the incognito recording with confirmation
     */
    const clearIncognitoRecordingWithConfirm = () => {
        if (incognitoRecording && incognitoRecording.value) {
            if (confirm(t('incognito.discardConfirm'))) {
                IncognitoStorage.clearIncognitoRecording();
                incognitoRecording.value = null;
                // If the incognito recording was selected, clear selection
                if (selectedRecording.value?.id === 'incognito') {
                    selectedRecording.value = null;
                }
                showToast(t('incognito.recordingDiscarded'), 'fa-trash');
            }
        }
    };

    /**
     * Select the incognito recording for viewing
     */
    const selectIncognitoRecording = () => {
        if (incognitoRecording && incognitoRecording.value) {
            selectedRecording.value = incognitoRecording.value;
            currentView.value = 'detail';
        }
    };

    /**
     * Load incognito recording from sessionStorage on app init
     */
    const loadIncognitoRecording = () => {
        const stored = IncognitoStorage.getIncognitoRecording();
        if (stored && incognitoRecording) {
            incognitoRecording.value = stored;
            console.log('[Incognito] Loaded recording from sessionStorage');
        }
    };

    /**
     * Check if there's an incognito recording (for navigation guards)
     */
    const hasIncognitoRecording = () => {
        return IncognitoStorage.hasIncognitoRecording();
    };

    return {
        handleDragOver,
        handleDragLeave,
        handleDrop,
        handleFileSelect,
        addFilesToQueue,
        removeFromQueue,
        cancelWaitingFile,
        clearCompletedUploads,
        startUpload,
        startProcessingQueue,
        resetCurrentFileProcessingState,
        startStatusPolling,
        pollProcessingStatus,
        addTagToSelection,
        removeTagFromSelection,
        applyTagDefaults,
        filteredAvailableTagsForUpload,
        // Tag drag-and-drop
        draggedTagIndex,
        dragOverTagIndex,
        handleTagDragStart,
        handleTagDragOver,
        handleTagDrop,
        handleTagDragEnd,
        handleTagTouchStart,
        handleTagTouchMove,
        handleTagTouchEnd,
        // Incognito mode
        startIncognitoUpload,
        clearIncognitoRecordingWithConfirm,
        selectIncognitoRecording,
        loadIncognitoRecording,
        hasIncognitoRecording
    };
}
