/**
 * OTIS Offline Sync Engine v2.0
 * Handles: IndexedDB queue, offline action capture, auto-sync on reconnect
 * UI: Status bar injection and live status updates
 */

const OTISOffline = (() => {
    const DB_NAME = 'otis_offline_db';
    const DB_VERSION = 2;
    const STORE_NAME = 'offline_queue';
    let db = null;
    let isOnline = navigator.onLine;
    let syncInProgress = false;

    // =============================================
    // INDEXEDDB SETUP
    // =============================================
    function initDB() {
        return new Promise((resolve, reject) => {
            const req = indexedDB.open(DB_NAME, DB_VERSION);
            req.onupgradeneeded = (e) => {
                const database = e.target.result;
                if (!database.objectStoreNames.contains(STORE_NAME)) {
                    const store = database.createObjectStore(STORE_NAME, {
                        keyPath: 'id',
                        autoIncrement: true
                    });
                    store.createIndex('status', 'status', { unique: false });
                    store.createIndex('module', 'module', { unique: false });
                    store.createIndex('created_at', 'created_at', { unique: false });
                }
            };
            req.onsuccess = (e) => { db = e.target.result; resolve(db); };
            req.onerror = (e) => { console.error('[OTIS Offline] DB error:', e); reject(e); };
        });
    }

    // =============================================
    // QUEUE MANAGEMENT
    // =============================================
    function enqueue(action) {
        return new Promise((resolve, reject) => {
            const tx = db.transaction(STORE_NAME, 'readwrite');
            const store = tx.objectStore(STORE_NAME);
            const record = {
                module: action.module,      // 'todo' | 'health' | 'finance'
                endpoint: action.endpoint,  // URL to POST to when online
                payload: action.payload,    // FormData-compatible object
                method: action.method || 'POST',
                status: 'pending',          // 'pending' | 'syncing' | 'done' | 'failed'
                created_at: new Date().toISOString(),
                retry_count: 0,
                idempotency_key: action.idempotency_key || generateKey()
            };
            const req = store.add(record);
            req.onsuccess = () => { updateBadgeCount(); resolve(req.result); };
            req.onerror = () => reject(req.error);
        });
    }

    function getQueue(statusFilter = 'pending') {
        return new Promise((resolve, reject) => {
            if (!db) { resolve([]); return; }
            const tx = db.transaction(STORE_NAME, 'readonly');
            const store = tx.objectStore(STORE_NAME);
            const index = store.index('status');
            const req = index.getAll(statusFilter);
            req.onsuccess = () => resolve(req.result);
            req.onerror = () => reject(req.error);
        });
    }

    function updateRecord(id, updates) {
        return new Promise((resolve, reject) => {
            const tx = db.transaction(STORE_NAME, 'readwrite');
            const store = tx.objectStore(STORE_NAME);
            const getReq = store.get(id);
            getReq.onsuccess = () => {
                const record = getReq.result;
                if (!record) { resolve(); return; }
                Object.assign(record, updates);
                store.put(record).onsuccess = resolve;
            };
            getReq.onerror = () => reject(getReq.error);
        });
    }

    function getQueueCount() {
        return new Promise((resolve) => {
            if (!db) { resolve(0); return; }
            const tx = db.transaction(STORE_NAME, 'readonly');
            const store = tx.objectStore(STORE_NAME);
            const index = store.index('status');
            const req = index.count('pending');
            req.onsuccess = () => resolve(req.result);
            req.onerror = () => resolve(0);
        });
    }

    // =============================================
    // SYNC ENGINE
    // =============================================
    async function syncQueue() {
        if (syncInProgress || !navigator.onLine) return;
        syncInProgress = true;
        setStatus('syncing');

        try {
            const pending = await getQueue('pending');
            if (pending.length === 0) {
                setStatus('synced');
                syncInProgress = false;
                return;
            }

            console.log(`[OTIS Offline] Syncing ${pending.length} queued actions...`);
            let successCount = 0;

            for (const action of pending) {
                try {
                    // Mark as syncing
                    await updateRecord(action.id, { status: 'syncing' });

                    // Build FormData from payload
                    const formData = new FormData();
                    for (const [key, val] of Object.entries(action.payload)) {
                        formData.append(key, val);
                    }
                    // Include idempotency key to prevent duplicates on server
                    formData.append('_idempotency_key', action.idempotency_key);
                    formData.append('_offline_sync', '1');

                    const res = await fetch(action.endpoint, {
                        method: action.method || 'POST',
                        headers: { 'X-Requested-With': 'XMLHttpRequest' },
                        body: formData
                    });

                    if (res.ok) {
                        await updateRecord(action.id, { status: 'done', synced_at: new Date().toISOString() });
                        successCount++;
                    } else {
                        const retries = (action.retry_count || 0) + 1;
                        const newStatus = retries >= 5 ? 'failed' : 'pending';
                        await updateRecord(action.id, { status: newStatus, retry_count: retries });
                    }
                } catch (itemErr) {
                    console.warn('[OTIS Offline] Sync item failed:', itemErr);
                    await updateRecord(action.id, {
                        status: 'pending',
                        retry_count: (action.retry_count || 0) + 1
                    });
                }
            }

            console.log(`[OTIS Offline] Sync complete. ${successCount}/${pending.length} synced.`);
            setStatus(successCount === pending.length ? 'synced' : 'partial');
            updateBadgeCount();

            // Notify user via toast
            if (successCount > 0 && typeof OTIS !== 'undefined') {
                OTIS.toast(
                    '⚡ مزامنة ناجحة',
                    `تم مزامنة ${successCount} إجراء بنجاح من الوضع الأوف لاين.`,
                    'success'
                );
            }

            // Reload current page content via PJAX
            if (successCount > 0 && typeof OTIS !== 'undefined') {
                setTimeout(() => OTIS.pjaxLoad(window.location.href, false), 1000);
            }
        } catch (err) {
            console.error('[OTIS Offline] Sync error:', err);
            setStatus('error');
        } finally {
            syncInProgress = false;
        }
    }

    // =============================================
    // STATUS BAR UI
    // =============================================
    function injectStatusBar() {
        if (document.getElementById('otis-offline-bar')) return;

        const bar = document.createElement('div');
        bar.id = 'otis-offline-bar';
        bar.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            z-index: 99999;
            padding: 8px 20px;
            font-family: 'Cairo', 'Tajawal', sans-serif;
            font-size: 13px;
            font-weight: 700;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
            transform: translateY(-100%);
            direction: rtl;
        `;
        document.body.appendChild(bar);
        return bar;
    }

    function setStatus(status) {
        const bar = document.getElementById('otis-offline-bar') || injectStatusBar();
        if (!bar) return;

        const configs = {
            offline: {
                bg: 'linear-gradient(90deg, #dc3545, #b91c1c)',
                icon: '📡',
                text: 'وضع الأوف لاين — بياناتك تُحفظ محلياً',
                show: true
            },
            syncing: {
                bg: 'linear-gradient(90deg, #f59e0b, #d97706)',
                icon: '🔄',
                text: 'جارٍ المزامنة...',
                show: true
            },
            synced: {
                bg: 'linear-gradient(90deg, #059669, #047857)',
                icon: '✅',
                text: 'تمت المزامنة بنجاح',
                show: true
            },
            partial: {
                bg: 'linear-gradient(90deg, #f59e0b, #d97706)',
                icon: '⚠️',
                text: 'تمت المزامنة جزئياً',
                show: true
            },
            error: {
                bg: 'linear-gradient(90deg, #dc3545, #b91c1c)',
                icon: '❌',
                text: 'فشلت المزامنة — سيتم إعادة المحاولة',
                show: true
            },
            online: { show: false }
        };

        const cfg = configs[status] || configs.online;

        if (cfg.show) {
            bar.style.background = cfg.bg;
            bar.style.color = '#fff';
            bar.innerHTML = `<span>${cfg.icon}</span><span>${cfg.text}</span>`;
            bar.style.transform = 'translateY(0)';
            // Auto-hide "synced" after 4s
            if (status === 'synced') {
                setTimeout(() => { bar.style.transform = 'translateY(-100%)'; }, 4000);
            }
        } else {
            bar.style.transform = 'translateY(-100%)';
        }
    }

    async function updateBadgeCount() {
        const count = await getQueueCount();
        const badge = document.getElementById('otis-offline-badge');
        if (!badge) {
            // Inject badge in sidebar if count > 0
            if (count > 0) {
                const navItems = document.querySelectorAll('.dashboard-nav');
                // Add a subtle indicator somewhere if needed
            }
        } else {
            badge.textContent = count;
            badge.style.display = count > 0 ? 'inline-block' : 'none';
        }
    }

    // =============================================
    // FORM CAPTURE (Offline Action Interception)
    // =============================================
    function setupFormCapture() {
        document.addEventListener('submit', async (e) => {
            const form = e.target;

            // Only capture forms marked with data-offline-capable
            if (!form.dataset.offlineCapable) return;
            if (navigator.onLine) return; // Online — let normal handler process

            e.preventDefault();
            e.stopImmediatePropagation();

            const module = form.dataset.module || 'general';
            const formData = new FormData(form);
            const payload = {};
            for (const [k, v] of formData.entries()) {
                payload[k] = v;
            }

            const action = {
                module: module,
                endpoint: form.action,
                payload: payload,
                method: form.method || 'POST',
                idempotency_key: generateKey()
            };

            try {
                await enqueue(action);
                setStatus('offline');
                updateBadgeCount();

                // Show offline confirmation
                showOfflineConfirm(module);

                // Reset form
                form.reset();
            } catch (err) {
                console.error('[OTIS Offline] Enqueue failed:', err);
                alert('فشل حفظ البيانات محلياً. يرجى المحاولة لاحقاً.');
            }
        }, true); // Use capture to intercept before PJAX engine
    }

    function showOfflineConfirm(module) {
        const moduleNames = {
            todo: 'مهمة', health: 'سجل صحي', finance: 'مصروف',
            habits: 'عادة', journal: 'يومية', general: 'بيانات'
        };
        const name = moduleNames[module] || 'بيانات';

        if (typeof OTIS !== 'undefined') {
            OTIS.toast(
                '💾 تم الحفظ محلياً',
                `تم حفظ ${name} في قائمة المزامنة. ستُرسل تلقائياً عند عودة الاتصال.`,
                'warning'
            );
        }
    }

    // =============================================
    // CONNECTION MONITORING
    // =============================================
    function setupConnectionMonitor() {
        window.addEventListener('online', async () => {
            isOnline = true;
            console.log('[OTIS Offline] Connection restored — starting sync');
            setStatus('syncing');
            await syncQueue();
        });

        window.addEventListener('offline', () => {
            isOnline = false;
            console.log('[OTIS Offline] Connection lost');
            setStatus('offline');
        });

        // Handle SW message to trigger sync
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.addEventListener('message', (event) => {
                if (event.data && event.data.type === 'OTIS_SYNC_NOW') {
                    syncQueue();
                }
            });
        }

        // Initial status
        if (!navigator.onLine) {
            setStatus('offline');
        }
    }

    // =============================================
    // SERVICE WORKER REGISTRATION
    // =============================================
    async function registerSW() {
        if (!('serviceWorker' in navigator)) {
            console.warn('[OTIS Offline] Service Workers not supported');
            return;
        }

        try {
            const reg = await navigator.serviceWorker.register('/OTIS/public/sw.js', {
                scope: '/OTIS/'
            });

            console.log('[OTIS Offline] SW registered:', reg.scope);

            // Handle updates
            reg.addEventListener('updatefound', () => {
                const newWorker = reg.installing;
                newWorker.addEventListener('statechange', () => {
                    if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
                        console.log('[OTIS Offline] New SW available');
                        // Silently update
                        newWorker.postMessage({ type: 'SKIP_WAITING' });
                    }
                });
            });

            // Try background sync registration
            if ('SyncManager' in window) {
                try {
                    await reg.sync.register('otis-offline-sync');
                } catch (e) { /* Sync not critical */ }
            }

            return reg;
        } catch (err) {
            console.error('[OTIS Offline] SW registration failed:', err);
        }
    }

    // =============================================
    // UTILITIES
    // =============================================
    function generateKey() {
        return 'otis_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
    }

    // =============================================
    // PUBLIC API
    // =============================================
    async function init() {
        try {
            await initDB();
            injectStatusBar();
            setupConnectionMonitor();
            setupFormCapture();

            // Check if there are pending items to sync
            if (navigator.onLine) {
                const count = await getQueueCount();
                if (count > 0) {
                    console.log(`[OTIS Offline] ${count} pending items found — syncing`);
                    setTimeout(syncQueue, 2000);
                }
            }

            await registerSW();
        } catch (err) {
            console.error('[OTIS Offline] Init error:', err);
        }
    }

    return {
        init,
        enqueue,
        syncQueue,
        getQueueCount,
        setStatus,
        isOnline: () => navigator.onLine
    };
})();

// Auto-initialize when DOM is ready
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => OTISOffline.init());
} else {
    OTISOffline.init();
}
