/**
 * POS Offline Manager
 * Handles offline detection, IndexedDB storage, data caching, and auto-sync
 */

var OfflineManager = (function() {
  var DB_NAME = 'pos_offline_db';
  var DB_VERSION = 2;
  var db = null;
  var isOnline = navigator.onLine;
  var syncInProgress = false;

  // ==================== IndexedDB Setup ====================

  function initDB() {
    return new Promise(function(resolve, reject) {
      var request = indexedDB.open(DB_NAME, DB_VERSION);

      request.onupgradeneeded = function(event) {
        var db = event.target.result;

        // Store for offline sales
        if (!db.objectStoreNames.contains('offline_sales')) {
          var salesStore = db.createObjectStore('offline_sales', { keyPath: 'offline_id', autoIncrement: true });
          salesStore.createIndex('status', 'status', { unique: false });
          salesStore.createIndex('created_at', 'created_at', { unique: false });
        }

        // Store for offline payments
        if (!db.objectStoreNames.contains('offline_payments')) {
          var payStore = db.createObjectStore('offline_payments', { keyPath: 'offline_id', autoIncrement: true });
          payStore.createIndex('status', 'status', { unique: false });
          payStore.createIndex('created_at', 'created_at', { unique: false });
        }

        // Store for cached product grid HTML (from pos/get_details)
        if (!db.objectStoreNames.contains('cached_product_grid')) {
          db.createObjectStore('cached_product_grid', { keyPath: 'cache_key' });
        }

        // Store for cached items data (for search/autocomplete)
        if (!db.objectStoreNames.contains('cached_items')) {
          var itemsStore = db.createObjectStore('cached_items', { keyPath: 'id' });
          itemsStore.createIndex('item_name', 'item_name', { unique: false });
          itemsStore.createIndex('item_code', 'item_code', { unique: false });
        }

        // Store for cached customers
        if (!db.objectStoreNames.contains('cached_customers')) {
          db.createObjectStore('cached_customers', { keyPath: 'id' });
        }
      };

      request.onsuccess = function(event) {
        db = event.target.result;
        resolve(db);
      };

      request.onerror = function(event) {
        console.error('IndexedDB error:', event.target.error);
        reject(event.target.error);
      };
    });
  }

  // ==================== Cache Product Grid HTML ====================

  function cacheProductGrid(cacheKey, htmlContent) {
    if (!db) return;
    var tx = db.transaction('cached_product_grid', 'readwrite');
    var store = tx.objectStore('cached_product_grid');
    store.put({
      cache_key: cacheKey,
      html: htmlContent,
      cached_at: new Date().toISOString()
    });
  }

  function getCachedProductGrid(cacheKey) {
    return new Promise(function(resolve) {
      if (!db) { resolve(null); return; }
      var tx = db.transaction('cached_product_grid', 'readonly');
      var store = tx.objectStore('cached_product_grid');
      var request = store.get(cacheKey);
      request.onsuccess = function() { resolve(request.result ? request.result.html : null); };
      request.onerror = function() { resolve(null); };
    });
  }

  // ==================== Cache Items for Search ====================

  function cacheItemsFromGrid() {
    // Extract item data from the product grid DOM elements on the page
    if (!db) return;
    var items = [];
    $('.item_box').each(function() {
      var $el = $(this);
      var id = $el.attr('data-item-id');
      if (!id) return;
      items.push({
        id: parseInt(id),
        item_name: $el.attr('data-item-name') || '',
        stock: parseFloat($el.attr('data-item-available-qty')) || 0,
        sales_price: parseFloat($el.attr('data-item-sales-price')) || 0,
        purchase_price: parseFloat($el.attr('data-purchase_price')) || 0,
        tax_id: $el.attr('data-item-tax-id') || '',
        tax_type: $el.attr('data-item-tax-type') || '',
        tax_value: $el.attr('data-item-tax-value') || '',
        tax_name: $el.attr('data-item-tax-name') || '',
        tax_amt: $el.attr('data-item-tax-amt') || '',
        discount_type: $el.attr('data-discount_type') || '',
        discount: $el.attr('data-discount') || '',
        item_code: $el.find('.search_item').text() || ''
      });
    });

    if (items.length === 0) return;

    var tx = db.transaction('cached_items', 'readwrite');
    var store = tx.objectStore('cached_items');
    items.forEach(function(item) {
      store.put(item);
    });
    console.log('Offline: Cached ' + items.length + ' items from grid');
  }

  function cacheItemsFromJSON(jsonItems) {
    // Cache items from autocomplete/search JSON response
    if (!db || !jsonItems || !jsonItems.length) return;
    var tx = db.transaction('cached_items', 'readwrite');
    var store = tx.objectStore('cached_items');
    jsonItems.forEach(function(item) {
      // Merge with existing data (don't overwrite full data with partial search data)
      var getReq = store.get(parseInt(item.id));
      getReq.onsuccess = function() {
        var existing = getReq.result || {};
        existing.id = parseInt(item.id);
        existing.item_name = item.value || item.item_name || existing.item_name || '';
        existing.item_code = item.item_code || existing.item_code || '';
        existing.stock = parseFloat(item.stock) || existing.stock || 0;
        store.put(existing);
      };
    });
  }

  function searchCachedItems(searchTerm) {
    return new Promise(function(resolve) {
      if (!db) { resolve([]); return; }
      var tx = db.transaction('cached_items', 'readonly');
      var store = tx.objectStore('cached_items');
      var request = store.getAll();
      request.onsuccess = function() {
        var allItems = request.result || [];
        if (!searchTerm) { resolve(allItems); return; }
        var term = searchTerm.toLowerCase();
        var results = allItems.filter(function(item) {
          return (item.item_name && item.item_name.toLowerCase().indexOf(term) !== -1) ||
                 (item.item_code && item.item_code.toLowerCase().indexOf(term) !== -1);
        });
        resolve(results.slice(0, 10));
      };
      request.onerror = function() { resolve([]); };
    });
  }

  function getAllCachedItems() {
    return new Promise(function(resolve) {
      if (!db) { resolve([]); return; }
      var tx = db.transaction('cached_items', 'readonly');
      var store = tx.objectStore('cached_items');
      var request = store.getAll();
      request.onsuccess = function() { resolve(request.result || []); };
      request.onerror = function() { resolve([]); };
    });
  }

  // ==================== Cache Customers ====================

  function cacheCustomersFromDOM() {
    if (!db) return;
    var customers = [];
    $('#customer_id option').each(function() {
      var id = $(this).val();
      var name = $(this).text().trim();
      if (id && name) {
        customers.push({ id: parseInt(id), customer_name: name });
      }
    });
    if (customers.length === 0) return;
    var tx = db.transaction('cached_customers', 'readwrite');
    var store = tx.objectStore('cached_customers');
    // Clear old and add new
    store.clear();
    customers.forEach(function(c) { store.put(c); });
    console.log('Offline: Cached ' + customers.length + ' customers');
  }

  // ==================== Bulk Cache: Fetch All Items ====================

  function fetchAndCacheAllItems() {
    // Fetch ALL items from server via the items JSON endpoint and cache them
    var baseUrl = getBaseUrl();
    $.ajax({
      url: baseUrl + 'items/get_json_items_details',
      method: 'GET',
      dataType: 'json',
      data: { name: '' }, // empty search = get all
      timeout: 30000,
      success: function(res) {
        if (res && res.length) {
          cacheItemsFromJSON(res);
          console.log('Offline: Bulk cached ' + res.length + ' items from server');
        }
      },
      error: function() {
        console.log('Offline: Could not bulk fetch items (may need to cache from grid instead)');
      }
    });
  }

  function fetchAndCacheProductGrid() {
    // Fetch the initial product grid HTML and cache it
    var baseUrl = getBaseUrl();
    $.ajax({
      url: baseUrl + 'pos/get_details',
      method: 'POST',
      data: { last_id: '', id: '', brand_id: '' },
      timeout: 30000,
      success: function(html) {
        if (html) {
          cacheProductGrid('default', html);
          console.log('Offline: Cached product grid HTML');
        }
      },
      error: function() {
        console.log('Offline: Could not cache product grid');
      }
    });
  }

  // ==================== Save Offline Sale ====================

  function saveOfflineSale(formData, queryParams) {
    return new Promise(function(resolve, reject) {
      if (!db) {
        reject('Database not initialized');
        return;
      }

      // Convert FormData to a plain object for storage
      var saleData = {};
      formData.forEach(function(value, key) {
        saleData[key] = value;
      });

      var record = {
        type: 'sale',
        data: saleData,
        queryParams: queryParams,
        status: 'pending',
        created_at: new Date().toISOString(),
        attempts: 0
      };

      var tx = db.transaction('offline_sales', 'readwrite');
      var store = tx.objectStore('offline_sales');
      var request = store.add(record);

      request.onsuccess = function() {
        record.offline_id = request.result;
        resolve(record);
        updatePendingBadge();
      };

      request.onerror = function() {
        reject('Failed to save offline sale');
      };
    });
  }

  // ==================== Save Offline Payment ====================

  function saveOfflinePayment(paymentData) {
    return new Promise(function(resolve, reject) {
      if (!db) {
        reject('Database not initialized');
        return;
      }

      var record = {
        type: 'payment',
        data: paymentData,
        status: 'pending',
        created_at: new Date().toISOString(),
        attempts: 0
      };

      var tx = db.transaction('offline_payments', 'readwrite');
      var store = tx.objectStore('offline_payments');
      var request = store.add(record);

      request.onsuccess = function() {
        record.offline_id = request.result;
        resolve(record);
        updatePendingBadge();
      };

      request.onerror = function() {
        reject('Failed to save offline payment');
      };
    });
  }

  // ==================== Get Pending Count ====================

  function getPendingCount() {
    return new Promise(function(resolve) {
      if (!db) { resolve(0); return; }

      var total = 0;

      var tx = db.transaction(['offline_sales', 'offline_payments'], 'readonly');

      var salesReq = tx.objectStore('offline_sales').index('status').count(IDBKeyRange.only('pending'));
      salesReq.onsuccess = function() { total += salesReq.result; };

      var payReq = tx.objectStore('offline_payments').index('status').count(IDBKeyRange.only('pending'));
      payReq.onsuccess = function() { total += payReq.result; };

      tx.oncomplete = function() { resolve(total); };
      tx.onerror = function() { resolve(0); };
    });
  }

  // ==================== Get All Pending Records ====================

  function getPendingRecords(storeName) {
    return new Promise(function(resolve) {
      if (!db) { resolve([]); return; }

      var tx = db.transaction(storeName, 'readonly');
      var store = tx.objectStore(storeName);
      var index = store.index('status');
      var request = index.getAll(IDBKeyRange.only('pending'));

      request.onsuccess = function() { resolve(request.result || []); };
      request.onerror = function() { resolve([]); };
    });
  }

  // ==================== Update Record Status ====================

  function updateRecordStatus(storeName, offlineId, status) {
    return new Promise(function(resolve) {
      if (!db) { resolve(); return; }

      var tx = db.transaction(storeName, 'readwrite');
      var store = tx.objectStore(storeName);
      var getReq = store.get(offlineId);

      getReq.onsuccess = function() {
        var record = getReq.result;
        if (record) {
          record.status = status;
          record.synced_at = new Date().toISOString();
          store.put(record);
        }
        resolve();
      };
      getReq.onerror = function() { resolve(); };
    });
  }

  // ==================== Sync Engine ====================

  function syncAll() {
    if (syncInProgress || !isOnline) return Promise.resolve();
    syncInProgress = true;

    updateSyncStatus('syncing');

    return syncSales()
      .then(function() { return syncPayments(); })
      .then(function() {
        syncInProgress = false;
        updatePendingBadge();
        updateSyncStatus('idle');
        // Refresh cached data after sync (only if still online)
        if (navigator.onLine) {
          fetchAndCacheProductGrid();
          fetchAndCacheAllItems();
        }
      })
      .catch(function(err) {
        console.error('Sync error:', err);
        syncInProgress = false;
        updateSyncStatus('error');
      });
  }

  function syncSales() {
    return getPendingRecords('offline_sales').then(function(records) {
      var chain = Promise.resolve();

      records.forEach(function(record) {
        chain = chain.then(function() {
          return syncSingleSale(record);
        });
      });

      return chain;
    });
  }

  function syncSingleSale(record) {
    var base_url = getBaseUrl();

    // Rebuild FormData from stored data
    var formData = new FormData();
    var saleData = record.data;
    for (var key in saleData) {
      if (saleData.hasOwnProperty(key)) {
        formData.append(key, saleData[key]);
      }
    }

    // Rebuild the CSRF token (get fresh one)
    var csrfName = $('meta[name="csrf-name"]').attr('content') || getCsrfName();
    var csrfHash = $('meta[name="csrf-hash"]').attr('content') || getCsrfHash();
    if (csrfName && csrfHash) {
      formData.append(csrfName, csrfHash);
    }

    return $.ajax({
      type: 'POST',
      url: base_url + 'pos/pos_save_update?' + record.queryParams,
      data: formData,
      cache: false,
      contentType: false,
      processData: false,
      timeout: 30000
    }).then(function(result) {
      result = result.trim().split("<<<###>>>");
      if (result[0] === 'success') {
        return updateRecordStatus('offline_sales', record.offline_id, 'synced').then(function() {
          showSyncNotification('Sale #' + record.offline_id + ' synced successfully!', 'success');
        });
      } else {
        record.attempts++;
        if (record.attempts >= 3) {
          return updateRecordStatus('offline_sales', record.offline_id, 'failed').then(function() {
            showSyncNotification('Sale #' + record.offline_id + ' sync failed after 3 attempts', 'error');
          });
        }
      }
    }).catch(function() {
      console.log('Sync failed for sale #' + record.offline_id + ', will retry later');
    });
  }

  function syncPayments() {
    return getPendingRecords('offline_payments').then(function(records) {
      var chain = Promise.resolve();

      records.forEach(function(record) {
        chain = chain.then(function() {
          return syncSinglePayment(record);
        });
      });

      return chain;
    });
  }

  function syncSinglePayment(record) {
    var base_url = getBaseUrl();
    var payData = $.extend({}, record.data);

    // Add fresh CSRF token
    var csrfName = getCsrfName();
    var csrfHash = getCsrfHash();
    if (csrfName && csrfHash) {
      payData[csrfName] = csrfHash;
    }

    return $.post({
      url: base_url + 'sales/save_payment',
      data: payData,
      timeout: 30000
    }).then(function(result) {
      result = result.trim();
      if (result === 'success') {
        return updateRecordStatus('offline_payments', record.offline_id, 'synced').then(function() {
          showSyncNotification('Payment #' + record.offline_id + ' synced successfully!', 'success');
        });
      } else {
        record.attempts++;
        if (record.attempts >= 3) {
          return updateRecordStatus('offline_payments', record.offline_id, 'failed').then(function() {
            showSyncNotification('Payment #' + record.offline_id + ' sync failed', 'error');
          });
        }
      }
    }).catch(function() {
      console.log('Sync failed for payment #' + record.offline_id + ', will retry later');
    });
  }

  // ==================== Helpers ====================

  function getBaseUrl() {
    var el = document.getElementById('base_url');
    if (el) return el.value.trim();
    return window.location.origin + window.location.pathname.replace(/\/[^\/]*$/, '/');
  }

  function getCsrfName() {
    var el = document.querySelector('input[name$="_token_name"]') ||
             document.querySelector('script[data-csrf-name]');
    if (el) return el.name || el.getAttribute('data-csrf-name');
    return null;
  }

  function getCsrfHash() {
    var el = document.querySelector('input[name$="_token_name"]');
    if (el) return el.value;
    return null;
  }

  // ==================== UI Functions ====================

  function createStatusBar() {
    if (document.getElementById('offline-status-bar')) return;

    var bar = document.createElement('div');
    bar.id = 'offline-status-bar';
    bar.innerHTML =
      '<div id="offline-indicator" class="offline-indicator">' +
        '<i class="fa fa-wifi"></i> ' +
        '<span id="offline-text">Online</span>' +
        '<span id="offline-pending-badge" class="offline-badge" style="display:none;">0</span>' +
        '<button id="offline-sync-btn" class="offline-sync-btn" style="display:none;" title="Sync pending transactions">' +
          '<i class="fa fa-refresh"></i> Sync Now' +
        '</button>' +
        '<button id="offline-view-btn" class="offline-view-btn" style="display:none;" title="View pending transactions">' +
          '<i class="fa fa-list"></i>' +
        '</button>' +
      '</div>';
    document.body.appendChild(bar);

    document.getElementById('offline-sync-btn').addEventListener('click', function() {
      syncAll();
    });

    document.getElementById('offline-view-btn').addEventListener('click', function() {
      showPendingModal();
    });

    updateStatusUI();
  }

  function updateStatusUI() {
    var indicator = document.getElementById('offline-indicator');
    var text = document.getElementById('offline-text');
    if (!indicator || !text) return;

    if (isOnline) {
      indicator.className = 'offline-indicator online';
      text.textContent = 'Online';
    } else {
      indicator.className = 'offline-indicator offline';
      text.textContent = 'Offline Mode';
    }
  }

  function updatePendingBadge() {
    getPendingCount().then(function(count) {
      var badge = document.getElementById('offline-pending-badge');
      var syncBtn = document.getElementById('offline-sync-btn');
      var viewBtn = document.getElementById('offline-view-btn');
      if (!badge) return;

      if (count > 0) {
        badge.textContent = count;
        badge.style.display = 'inline-block';
        if (syncBtn) syncBtn.style.display = isOnline ? 'inline-block' : 'none';
        if (viewBtn) viewBtn.style.display = 'inline-block';
      } else {
        badge.style.display = 'none';
        if (syncBtn) syncBtn.style.display = 'none';
        if (viewBtn) viewBtn.style.display = 'none';
      }
    });
  }

  function updateSyncStatus(status) {
    var syncBtn = document.getElementById('offline-sync-btn');
    if (!syncBtn) return;

    if (status === 'syncing') {
      syncBtn.innerHTML = '<i class="fa fa-refresh fa-spin"></i> Syncing...';
      syncBtn.disabled = true;
    } else {
      syncBtn.innerHTML = '<i class="fa fa-refresh"></i> Sync Now';
      syncBtn.disabled = false;
    }
  }

  function showSyncNotification(message, type) {
    if (typeof toastr !== 'undefined') {
      toastr[type](message);
    }
  }

  function showPendingModal() {
    Promise.all([
      getPendingRecords('offline_sales'),
      getPendingRecords('offline_payments')
    ]).then(function(results) {
      var sales = results[0];
      var payments = results[1];

      var html = '<div class="modal fade" id="offline-pending-modal" tabindex="-1">' +
        '<div class="modal-dialog modal-lg"><div class="modal-content">' +
        '<div class="modal-header bg-info">' +
        '<button type="button" class="close" data-dismiss="modal">&times;</button>' +
        '<h4 class="modal-title"><i class="fa fa-clock-o"></i> Pending Offline Transactions</h4></div>' +
        '<div class="modal-body">';

      if (sales.length === 0 && payments.length === 0) {
        html += '<p class="text-center text-muted">No pending transactions</p>';
      }

      if (sales.length > 0) {
        html += '<h4><i class="fa fa-shopping-cart"></i> Pending Sales (' + sales.length + ')</h4>' +
          '<table class="table table-striped table-bordered"><thead><tr>' +
          '<th>#</th><th>Customer ID</th><th>Grand Total</th><th>Date Saved</th><th>Status</th></tr></thead><tbody>';
        sales.forEach(function(s, i) {
          var grandTotal = s.queryParams ? (s.queryParams.match(/tot_grand=([^&]*)/) || [])[1] || 'N/A' : 'N/A';
          html += '<tr><td>' + (i + 1) + '</td>' +
            '<td>' + (s.data.customer_id || 'N/A') + '</td>' +
            '<td>' + grandTotal + '</td>' +
            '<td>' + new Date(s.created_at).toLocaleString() + '</td>' +
            '<td><span class="label label-warning">' + s.status + '</span></td></tr>';
        });
        html += '</tbody></table>';
      }

      if (payments.length > 0) {
        html += '<h4><i class="fa fa-money"></i> Pending Payments (' + payments.length + ')</h4>' +
          '<table class="table table-striped table-bordered"><thead><tr>' +
          '<th>#</th><th>Sales ID</th><th>Amount</th><th>Type</th><th>Date Saved</th><th>Status</th></tr></thead><tbody>';
        payments.forEach(function(p, i) {
          html += '<tr><td>' + (i + 1) + '</td>' +
            '<td>' + (p.data.sales_id || 'N/A') + '</td>' +
            '<td>' + (p.data.amount || 'N/A') + '</td>' +
            '<td>' + (p.data.payment_type || 'N/A') + '</td>' +
            '<td>' + new Date(p.created_at).toLocaleString() + '</td>' +
            '<td><span class="label label-warning">' + p.status + '</span></td></tr>';
        });
        html += '</tbody></table>';
      }

      html += '</div><div class="modal-footer">' +
        '<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>';
      if (isOnline && (sales.length > 0 || payments.length > 0)) {
        html += '<button type="button" class="btn btn-primary" onclick="OfflineManager.sync();$(\'#offline-pending-modal\').modal(\'hide\');">' +
          '<i class="fa fa-refresh"></i> Sync All Now</button>';
      }
      html += '</div></div></div></div>';

      $('#offline-pending-modal').remove();
      $('body').append(html);
      $('#offline-pending-modal').modal('show');
    });
  }

  // ==================== Network Event Listeners ====================

  function setupNetworkListeners() {
    window.addEventListener('online', function() {
      isOnline = true;
      updateStatusUI();
      showSyncNotification('Internet connection restored!', 'success');

      // Auto-sync after short delay
      setTimeout(function() {
        syncAll();
      }, 2000);
    });

    window.addEventListener('offline', function() {
      isOnline = false;
      updateStatusUI();
      updatePendingBadge();
      showSyncNotification('You are offline. Sales & payments will be saved locally.', 'warning');
    });
  }

  // ==================== Pre-Cache Pages ====================

  function preCacheCriticalPages() {
    if (!navigator.serviceWorker || !navigator.serviceWorker.controller) return;

    var baseUrl = getBaseUrl();
    var criticalPages = [
      baseUrl + 'pos',
      baseUrl + 'sales',
      baseUrl + 'dashboard'
    ];

    navigator.serviceWorker.controller.postMessage({
      type: 'CACHE_PAGES',
      urls: criticalPages
    });

    console.log('Offline: Requested pre-cache of critical pages');
  }

  // ==================== Initialize ====================

  function init() {
    return initDB().then(function() {
      createStatusBar();
      setupNetworkListeners();
      updatePendingBadge();

      if (isOnline) {
        setTimeout(function() {
          if (!navigator.onLine) return;
          syncAll();
        }, 3000);
        // Pre-cache pages for offline navigation
        setTimeout(function() {
          if (!navigator.onLine) return;
          preCacheCriticalPages();
        }, 5000);
        // Cache product data for offline POS use
        setTimeout(function() {
          if (!navigator.onLine) return;
          fetchAndCacheProductGrid();
          fetchAndCacheAllItems();
          cacheItemsFromGrid();
          cacheCustomersFromDOM();
        }, 6000);
      }

      console.log('POS Offline Manager initialized');
    }).catch(function(err) {
      console.error('Failed to initialize offline manager:', err);
    });
  }

  // ==================== Public API ====================

  return {
    init: init,
    isOnline: function() { return isOnline; },
    saveSale: saveOfflineSale,
    savePayment: saveOfflinePayment,
    sync: syncAll,
    getPendingCount: getPendingCount,
    showPending: showPendingModal,
    // Data cache API (used by pos.php inline scripts)
    cacheProductGrid: cacheProductGrid,
    getCachedProductGrid: getCachedProductGrid,
    cacheItemsFromGrid: cacheItemsFromGrid,
    cacheItemsFromJSON: cacheItemsFromJSON,
    searchCachedItems: searchCachedItems,
    getAllCachedItems: getAllCachedItems,
    cacheCustomersFromDOM: cacheCustomersFromDOM
  };

})();

// Auto-initialize when DOM is ready
$(document).ready(function() {
  OfflineManager.init();
});
