/**
 * Offline Sync Manager for TimeTracker
 * Handles offline data storage, sync queue, and conflict resolution
 */

class OfflineSyncManager {
    constructor() {
        this.dbName = 'TimeTrackerDB';
        this.dbVersion = 2;
        this.db = null;
        this.syncInProgress = false;
        this.pendingSyncCount = 0;
        this.init();
    }

    async init() {
        try {
            this.db = await this.openDB();
            this.setupOnlineListener();
            this.setupServiceWorkerSync();
            await this.checkPendingSync();
            this.updateUI();
        } catch (error) {
            console.error('[OfflineSync] Initialization failed:', error);
        }
    }

    openDB() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open(this.dbName, this.dbVersion);

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

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

                // Time entries store
                if (!db.objectStoreNames.contains('timeEntries')) {
                    const store = db.createObjectStore('timeEntries', {
                        keyPath: 'localId',
                        autoIncrement: true
                    });
                    store.createIndex('serverId', 'serverId', { unique: false });
                    store.createIndex('timestamp', 'timestamp', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Tasks store
                if (!db.objectStoreNames.contains('tasks')) {
                    const store = db.createObjectStore('tasks', {
                        keyPath: 'localId',
                        autoIncrement: true
                    });
                    store.createIndex('serverId', 'serverId', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Projects store
                if (!db.objectStoreNames.contains('projects')) {
                    const store = db.createObjectStore('projects', {
                        keyPath: 'localId',
                        autoIncrement: true
                    });
                    store.createIndex('serverId', 'serverId', { unique: false });
                    store.createIndex('synced', 'synced', { unique: false });
                }

                // Sync queue store
                if (!db.objectStoreNames.contains('syncQueue')) {
                    const store = db.createObjectStore('syncQueue', {
                        keyPath: 'id',
                        autoIncrement: true
                    });
                    store.createIndex('type', 'type', { unique: false });
                    store.createIndex('timestamp', 'timestamp', { unique: false });
                    store.createIndex('processed', 'processed', { unique: false });
                }
            };
        });
    }

    setupOnlineListener() {
        window.addEventListener('online', () => {
            this.syncAll();
        });

        window.addEventListener('offline', () => {
            this.updateUI();
        });
    }

    setupServiceWorkerSync() {
        if ('serviceWorker' in navigator && 'sync' in self.ServiceWorkerRegistration.prototype) {
            navigator.serviceWorker.ready.then(registration => {
                // Register background sync
                registration.sync.register('sync-time-entries').catch(err => {
                    // no-op
                });
            });
        }
    }

    async checkPendingSync() {
        if (!this.db) return;

        try {
            const count = await this.getPendingSyncCount();
            this.pendingSyncCount = count;
            this.updateUI();

            if (count > 0 && navigator.onLine) {
                // Auto-sync if online
                this.syncAll();
            }
        } catch (error) {
            console.error('[OfflineSync] Error checking pending sync:', error);
        }
    }

    async getPendingSyncCount() {
        return new Promise((resolve, reject) => {
            try {
                const transaction = this.db.transaction(['syncQueue'], 'readonly');
                const store = transaction.objectStore('syncQueue');
                
                // Check if the index exists
                if (!store.indexNames.contains('processed')) {
                    // If index doesn't exist, count manually
                    const request = store.openCursor();
                    let count = 0;
                    request.onsuccess = (event) => {
                        const cursor = event.target.result;
                        if (cursor) {
                            if (cursor.value.processed === false || !cursor.value.processed) {
                                count++;
                            }
                            cursor.continue();
                        } else {
                            resolve(count);
                        }
                    };
                    request.onerror = () => reject(request.error);
                    return;
                }
                
                const index = store.index('processed');
                // IndexedDB doesn't support boolean values in IDBKeyRange, so we use a cursor approach
                // Iterate through all items and filter for processed === false
                const request = index.openCursor();
                let count = 0;
                
                request.onsuccess = (event) => {
                    const cursor = event.target.result;
                    if (cursor) {
                        // Check if the value is false (unprocessed)
                        if (cursor.value.processed === false || cursor.value.processed === 0 || !cursor.value.processed) {
                            count++;
                        }
                        cursor.continue();
                    } else {
                        resolve(count);
                    }
                };
                
                request.onerror = () => {
                    // Fallback: count manually if index query fails
                    const fallbackRequest = store.openCursor();
                    let fallbackCount = 0;
                    fallbackRequest.onsuccess = (event) => {
                        const cursor = event.target.result;
                        if (cursor) {
                            if (cursor.value.processed === false || !cursor.value.processed) {
                                fallbackCount++;
                            }
                            cursor.continue();
                        } else {
                            resolve(fallbackCount);
                        }
                    };
                    fallbackRequest.onerror = () => reject(fallbackRequest.error);
                };
            } catch (error) {
                // If there's any error, return 0 instead of rejecting
                console.warn('[OfflineSync] Error counting pending sync, returning 0:', error);
                resolve(0);
            }
        });
    }

    // Helper function to format dates to ISO 8601
    formatDateToISO(dateValue) {
        if (!dateValue) return null;
        
        // If it's already a string in ISO format, return as is
        if (typeof dateValue === 'string') {
            // Check if it's already in ISO format
            if (dateValue.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/)) {
                return dateValue;
            }
            // Try to parse and reformat
            try {
                const date = new Date(dateValue);
                if (!isNaN(date.getTime())) {
                    return date.toISOString();
                }
            } catch (e) {
                console.error('[OfflineSync] Error parsing date string:', dateValue, e);
            }
            return dateValue;
        }
        
        // If it's a Date object, convert to ISO string
        if (dateValue instanceof Date) {
            if (isNaN(dateValue.getTime())) {
                console.error('[OfflineSync] Invalid Date object:', dateValue);
                return null;
            }
            return dateValue.toISOString();
        }
        
        // Fallback: try to create a Date object
        try {
            const date = new Date(dateValue);
            if (!isNaN(date.getTime())) {
                return date.toISOString();
            }
        } catch (e) {
            console.error('[OfflineSync] Error formatting date:', dateValue, e);
        }
        
        return null;
    }

    // Time Entry Operations
    async saveTimeEntryOffline(entryData) {
        if (!this.db) {
            throw new Error('Database not initialized');
        }

        // Normalize dates to ISO format for consistent storage
        const normalizedData = {
            ...entryData,
            start_time: this.formatDateToISO(entryData.start_time),
            end_time: entryData.end_time ? this.formatDateToISO(entryData.end_time) : null
        };

        const entry = {
            ...normalizedData,
            localId: `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
            serverId: null,
            synced: false,
            timestamp: new Date().toISOString(),
            conflict: false
        };

        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(['timeEntries', 'syncQueue'], 'readwrite');
            const entriesStore = transaction.objectStore('timeEntries');
            const queueStore = transaction.objectStore('syncQueue');

            const addRequest = entriesStore.add(entry);

            addRequest.onsuccess = () => {
                // Add to sync queue
                const queueItem = {
                    type: 'time_entry',
                    action: 'create',
                    localId: entry.localId,
                    data: normalizedData,
                    timestamp: new Date().toISOString(),
                    processed: false,
                    retries: 0
                };

                queueStore.add(queueItem).onsuccess = () => {
                    this.pendingSyncCount++;
                    this.updateUI();
                    resolve(entry);
                };
            };

            addRequest.onerror = () => reject(addRequest.error);
        });
    }

    async getOfflineTimeEntries() {
        if (!this.db) return [];

        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(['timeEntries'], 'readonly');
            const store = transaction.objectStore('timeEntries');
            const request = store.getAll();

            request.onerror = () => reject(request.error);
            request.onsuccess = () => resolve(request.result || []);
        });
    }

    // Sync Operations
    async syncAll() {
        if (!navigator.onLine || this.syncInProgress) {
            return;
        }

        this.syncInProgress = true;
        this.updateUI();

        try {
            await this.syncTimeEntries();
            await this.syncTasks();
            await this.syncProjects();
            await this.processSyncQueue();

            await this.checkPendingSync();
        } catch (error) {
            console.error('[OfflineSync] Sync error:', error);
        } finally {
            this.syncInProgress = false;
            this.updateUI();
        }
    }

    async syncTimeEntries() {
        if (!this.db) return;

        const unsyncedEntries = await this.getUnsyncedEntries('timeEntries');

        for (const entry of unsyncedEntries) {
            try {
                // Format dates to ISO 8601
                const startTimeISO = this.formatDateToISO(entry.start_time);
                const endTimeISO = this.formatDateToISO(entry.end_time);
                
                if (!startTimeISO) {
                    console.error('[OfflineSync] Invalid start_time format:', entry.start_time);
                    continue;
                }

                const response = await fetch('/api/v1/time-entries', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        project_id: entry.project_id,
                        task_id: entry.task_id,
                        start_time: startTimeISO,
                        end_time: endTimeISO,
                        notes: entry.notes,
                        tags: entry.tags,
                        billable: entry.billable
                    })
                });

                if (response.ok) {
                    const result = await response.json();
                    await this.markAsSynced('timeEntries', entry.localId, result.id);
                    this.pendingSyncCount--;
                } else {
                    const errorText = await response.text();
                    console.error('[OfflineSync] Failed to sync entry:', response.status, response.statusText, errorText);
                }
            } catch (error) {
                console.error('[OfflineSync] Error syncing entry:', error);
            }
        }

        this.updateUI();
    }

    async syncTasks() {
        if (!this.db) return;

        const unsyncedTasks = await this.getUnsyncedEntries('tasks');

        for (const task of unsyncedTasks) {
            try {
                const taskData = {
                    name: task.name,
                    project_id: task.project_id,
                    description: task.description,
                    status: task.status,
                    priority: task.priority,
                    assigned_to: task.assigned_to,
                    due_date: task.due_date ? this.formatDateToISO(task.due_date) : null,
                    estimated_hours: task.estimated_hours,
                    notes: task.notes
                };

                let response;
                if (task.serverId) {
                    // Update existing task
                    response = await fetch(`/api/v1/tasks/${task.serverId}`, {
                        method: 'PUT',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(taskData)
                    });
                } else {
                    // Create new task
                    response = await fetch('/api/v1/tasks', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(taskData)
                    });
                }

                if (response.ok) {
                    const result = await response.json();
                    const taskId = result.task?.id || result.id;
                    if (taskId) {
                        await this.markAsSynced('tasks', task.localId, taskId);
                        this.pendingSyncCount--;
                    }
                } else {
                    const errorText = await response.text();
                    console.error('[OfflineSync] Failed to sync task:', response.status, response.statusText, errorText);
                }
            } catch (error) {
                console.error('[OfflineSync] Error syncing task:', error);
            }
        }

        this.updateUI();
    }

    async syncProjects() {
        if (!this.db) return;

        const unsyncedProjects = await this.getUnsyncedEntries('projects');

        for (const project of unsyncedProjects) {
            try {
                const projectData = {
                    name: project.name,
                    description: project.description,
                    client_id: project.client_id,
                    status: project.status || 'active',
                    billable: project.billable !== false,
                    hourly_rate: project.hourly_rate,
                    code: project.code,
                    budget_amount: project.budget_amount,
                    budget_threshold_percent: project.budget_threshold_percent,
                    billing_ref: project.billing_ref
                };

                let response;
                if (project.serverId) {
                    // Update existing project
                    response = await fetch(`/api/v1/projects/${project.serverId}`, {
                        method: 'PUT',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(projectData)
                    });
                } else {
                    // Create new project
                    response = await fetch('/api/v1/projects', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify(projectData)
                    });
                }

                if (response.ok) {
                    const result = await response.json();
                    const projectId = result.project?.id || result.id;
                    if (projectId) {
                        await this.markAsSynced('projects', project.localId, projectId);
                        this.pendingSyncCount--;
                    }
                } else {
                    const errorText = await response.text();
                    console.error('[OfflineSync] Failed to sync project:', response.status, response.statusText, errorText);
                }
            } catch (error) {
                console.error('[OfflineSync] Error syncing project:', error);
            }
        }

        this.updateUI();
    }

    async getUnsyncedEntries(storeName) {
        return new Promise((resolve, reject) => {
            try {
                const transaction = this.db.transaction([storeName], 'readonly');
                const store = transaction.objectStore(storeName);
                
                // Check if the index exists
                if (!store.indexNames.contains('synced')) {
                    // If index doesn't exist, filter manually
                    const request = store.getAll();
                    request.onsuccess = () => {
                        const all = request.result || [];
                        const unsynced = all.filter(entry => entry.synced === false || !entry.synced);
                        resolve(unsynced);
                    };
                    request.onerror = () => reject(request.error);
                    return;
                }
                
                const index = store.index('synced');
                // IndexedDB doesn't support boolean values in IDBKeyRange, so we use a cursor approach
                // Iterate through all items and filter for synced === false
                const request = index.openCursor();
                const results = [];
                
                request.onsuccess = (event) => {
                    const cursor = event.target.result;
                    if (cursor) {
                        // Check if the value is false (unsynced)
                        if (cursor.value.synced === false || cursor.value.synced === 0 || !cursor.value.synced) {
                            results.push(cursor.value);
                        }
                        cursor.continue();
                    } else {
                        resolve(results);
                    }
                };
                
                request.onerror = () => {
                    // Fallback: filter manually if index query fails
                    const fallbackRequest = store.getAll();
                    fallbackRequest.onsuccess = () => {
                        const all = fallbackRequest.result || [];
                        const unsynced = all.filter(entry => entry.synced === false || !entry.synced);
                        resolve(unsynced);
                    };
                    fallbackRequest.onerror = () => reject(fallbackRequest.error);
                };
            } catch (error) {
                console.warn('[OfflineSync] Error getting unsynced entries, returning empty array:', error);
                resolve([]);
            }
        });
    }

    async markAsSynced(storeName, localId, serverId) {
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction([storeName, 'syncQueue'], 'readwrite');
            const store = transaction.objectStore(storeName);
            const queueStore = transaction.objectStore('syncQueue');

            const getRequest = store.get(localId);
            getRequest.onsuccess = () => {
                const entry = getRequest.result;
                if (entry) {
                    entry.serverId = serverId;
                    entry.synced = true;
                    entry.syncedAt = new Date().toISOString();

                    const putRequest = store.put(entry);
                    putRequest.onsuccess = () => {
                        // Mark queue item as processed
                        const index = queueStore.index('type');
                        // Determine queue type based on store name
                        let queueType = 'time_entry';
                        if (storeName === 'tasks') {
                            queueType = 'task';
                        } else if (storeName === 'projects') {
                            queueType = 'project';
                        }
                        
                        const queueRequest = index.openCursor(IDBKeyRange.only(queueType));
                        queueRequest.onsuccess = (event) => {
                            const cursor = event.target.result;
                            if (cursor) {
                                if (cursor.value.localId === localId) {
                                    cursor.value.processed = true;
                                    cursor.update(cursor.value);
                                }
                                cursor.continue();
                            } else {
                                resolve();
                            }
                        };
                        queueRequest.onerror = () => resolve(); // Resolve even if queue update fails
                    };
                    putRequest.onerror = () => reject(putRequest.error);
                } else {
                    resolve();
                }
            };
            getRequest.onerror = () => reject(getRequest.error);
        });
    }

    async processSyncQueue() {
        if (!this.db) return;

        return new Promise((resolve, reject) => {
            try {
                const transaction = this.db.transaction(['syncQueue'], 'readwrite');
                const store = transaction.objectStore('syncQueue');
                
                // Check if the index exists
                if (!store.indexNames.contains('processed')) {
                    // If index doesn't exist, iterate manually
                    const request = store.openCursor();
                    request.onsuccess = async (event) => {
                        const cursor = event.target.result;
                        if (cursor) {
                            const item = cursor.value;
                            // Process queue item based on type
                            // This will be handled by specific sync methods
                            if (item.processed === false || !item.processed) {
                                // Process unprocessed items
                            }
                            cursor.continue();
                        } else {
                            resolve();
                        }
                    };
                    request.onerror = () => reject(request.error);
                    return;
                }
                
                const index = store.index('processed');
                // IndexedDB doesn't support boolean values in IDBKeyRange, so we use a cursor approach
                const request = index.openCursor();

                request.onerror = () => {
                    // Fallback: iterate manually if index query fails
                    const fallbackRequest = store.openCursor();
                    fallbackRequest.onsuccess = async (event) => {
                        const cursor = event.target.result;
                        if (cursor) {
                            const item = cursor.value;
                            if (item.processed === false || !item.processed) {
                                // Process queue item based on type
                                // This will be handled by specific sync methods
                            }
                            cursor.continue();
                        } else {
                            resolve();
                        }
                    };
                    fallbackRequest.onerror = () => reject(fallbackRequest.error);
                };
                
                request.onsuccess = async (event) => {
                    const cursor = event.target.result;
                    if (cursor) {
                        const item = cursor.value;
                        // Check if the item is unprocessed
                        if (item.processed === false || item.processed === 0 || !item.processed) {
                            // Process queue item based on type
                            // This will be handled by specific sync methods
                        }
                        cursor.continue();
                    } else {
                        resolve();
                    }
                };
            } catch (error) {
                console.warn('[OfflineSync] Error processing sync queue:', error);
                resolve(); // Resolve instead of reject to prevent blocking
            }
        });
    }

    updateUI() {
        const isOnline = navigator.onLine;
        const hasPending = this.pendingSyncCount > 0;
        const isSyncing = this.syncInProgress;

        // Update offline indicator
        const indicator = document.getElementById('offline-indicator');
        const indicatorText = document.getElementById('offline-indicator-text');
        const indicatorIcon = document.getElementById('offline-indicator-icon');
        const syncButton = document.getElementById('offline-sync-button');
        
        if (indicator) {
            if (!isOnline) {
                indicator.classList.remove('hidden');
                indicator.className = indicator.className.replace(/bg-\w+-\d+ dark:bg-\w+-\d+/, 'bg-yellow-500 dark:bg-yellow-600');
                if (indicatorText) indicatorText.textContent = 'You are offline. Changes will sync when you reconnect.';
                if (indicatorIcon) indicatorIcon.className = 'fas fa-wifi-slash';
                if (syncButton) syncButton.style.display = 'none';
            } else if (hasPending && !isSyncing) {
                indicator.classList.remove('hidden');
                indicator.className = indicator.className.replace(/bg-\w+-\d+ dark:bg-\w+-\d+/, 'bg-blue-500 dark:bg-blue-600');
                if (indicatorText) indicatorText.textContent = `${this.pendingSyncCount} item(s) pending sync.`;
                if (indicatorIcon) indicatorIcon.className = 'fas fa-clock';
                if (syncButton) syncButton.style.display = 'inline-block';
            } else if (isSyncing) {
                indicator.classList.remove('hidden');
                indicator.className = indicator.className.replace(/bg-\w+-\d+ dark:bg-\w+-\d+/, 'bg-blue-500 dark:bg-blue-600');
                if (indicatorText) indicatorText.textContent = 'Syncing...';
                if (indicatorIcon) indicatorIcon.className = 'fas fa-sync-alt fa-spin';
                if (syncButton) syncButton.style.display = 'none';
            } else {
                indicator.classList.add('hidden');
            }
        }

        // Dispatch event for other components
        window.dispatchEvent(new CustomEvent('offlineSyncStatus', {
            detail: {
                online: isOnline,
                pendingCount: this.pendingSyncCount,
                syncing: isSyncing
            }
        }));
    }

    // Task Operations
    async saveTaskOffline(taskData) {
        if (!this.db) {
            throw new Error('Database not initialized');
        }

        const normalizedData = {
            ...taskData,
            due_date: taskData.due_date ? this.formatDateToISO(taskData.due_date) : null
        };

        const task = {
            ...normalizedData,
            localId: `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
            serverId: null,
            synced: false,
            timestamp: new Date().toISOString(),
            conflict: false
        };

        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(['tasks', 'syncQueue'], 'readwrite');
            const tasksStore = transaction.objectStore('tasks');
            const queueStore = transaction.objectStore('syncQueue');

            const addRequest = tasksStore.add(task);

            addRequest.onsuccess = () => {
                const queueItem = {
                    type: 'task',
                    action: task.serverId ? 'update' : 'create',
                    localId: task.localId,
                    data: normalizedData,
                    timestamp: new Date().toISOString(),
                    processed: false,
                    retries: 0
                };

                queueStore.add(queueItem).onsuccess = () => {
                    this.pendingSyncCount++;
                    this.updateUI();
                    resolve(task);
                };
            };

            addRequest.onerror = () => reject(addRequest.error);
        });
    }

    async getOfflineTasks() {
        if (!this.db) return [];

        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(['tasks'], 'readonly');
            const store = transaction.objectStore('tasks');
            const request = store.getAll();

            request.onerror = () => reject(request.error);
            request.onsuccess = () => resolve(request.result || []);
        });
    }

    // Project Operations
    async saveProjectOffline(projectData) {
        if (!this.db) {
            throw new Error('Database not initialized');
        }

        const project = {
            ...projectData,
            localId: `local_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
            serverId: null,
            synced: false,
            timestamp: new Date().toISOString(),
            conflict: false
        };

        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(['projects', 'syncQueue'], 'readwrite');
            const projectsStore = transaction.objectStore('projects');
            const queueStore = transaction.objectStore('syncQueue');

            const addRequest = projectsStore.add(project);

            addRequest.onsuccess = () => {
                const queueItem = {
                    type: 'project',
                    action: project.serverId ? 'update' : 'create',
                    localId: project.localId,
                    data: projectData,
                    timestamp: new Date().toISOString(),
                    processed: false,
                    retries: 0
                };

                queueStore.add(queueItem).onsuccess = () => {
                    this.pendingSyncCount++;
                    this.updateUI();
                    resolve(project);
                };
            };

            addRequest.onerror = () => reject(addRequest.error);
        });
    }

    async getOfflineProjects() {
        if (!this.db) return [];

        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(['projects'], 'readonly');
            const store = transaction.objectStore('projects');
            const request = store.getAll();

            request.onerror = () => reject(request.error);
            request.onsuccess = () => resolve(request.result || []);
        });
    }

    // Public API
    async createTimeEntryOffline(data) {
        // Normalize dates to ISO format
        const normalizedData = {
            ...data,
            start_time: this.formatDateToISO(data.start_time),
            end_time: data.end_time ? this.formatDateToISO(data.end_time) : null
        };

        if (navigator.onLine) {
            // Try online first
            try {
                const response = await fetch('/api/v1/time-entries', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(normalizedData)
                });

                if (response.ok) {
                    return await response.json();
                } else {
                    const errorText = await response.text();
                    console.error('[OfflineSync] Online create failed:', response.status, response.statusText, errorText);
                }
            } catch (error) {
                // no-op
            }
        }

        // Save offline
        return await this.saveTimeEntryOffline(normalizedData);
    }

    async createTaskOffline(data) {
        if (navigator.onLine) {
            // Try online first
            try {
                const response = await fetch('/api/v1/tasks', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(data)
                });

                if (response.ok) {
                    return await response.json();
                } else {
                    const errorText = await response.text();
                    console.error('[OfflineSync] Online task create failed:', response.status, response.statusText, errorText);
                }
            } catch (error) {
                // no-op
            }
        }

        // Save offline
        return await this.saveTaskOffline(data);
    }

    async createProjectOffline(data) {
        if (navigator.onLine) {
            // Try online first
            try {
                const response = await fetch('/api/v1/projects', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(data)
                });

                if (response.ok) {
                    return await response.json();
                } else {
                    const errorText = await response.text();
                    console.error('[OfflineSync] Online project create failed:', response.status, response.statusText, errorText);
                }
            } catch (error) {
                // no-op
            }
        }

        // Save offline
        return await this.saveProjectOffline(data);
    }

    async getPendingCount() {
        return this.pendingSyncCount;
    }

    async forceSync() {
        await this.syncAll();
    }
}

// Initialize singleton
window.offlineSyncManager = new OfflineSyncManager();

