(function () {
  'use strict';
  var CFG = window.PAINTOPS_TC || {};
  if (!CFG.ajax || !('indexedDB' in window)) { return; }

  var DB_NAME = 'paintops-offline';
  var STORE = 'timeclock_outbox';
  var reconciled = false;

  // ---- tiny IndexedDB helpers -------------------------------------
  function openDB() {
    return new Promise(function (resolve, reject) {
      var req = indexedDB.open(DB_NAME, 1);
      req.onupgradeneeded = function () {
        var db = req.result;
        if (!db.objectStoreNames.contains(STORE)) {
          db.createObjectStore(STORE, { keyPath: 'op_id' });
        }
      };
      req.onsuccess = function () { resolve(req.result); };
      req.onerror = function () { reject(req.error); };
    });
  }
  function store(db, mode) { return db.transaction(STORE, mode).objectStore(STORE); }
  function idbPut(rec) {
    return openDB().then(function (db) {
      return new Promise(function (res, rej) {
        var r = store(db, 'readwrite').put(rec);
        r.onsuccess = function () { res(); };
        r.onerror = function () { rej(r.error); };
      });
    });
  }
  function idbDelete(id) {
    return openDB().then(function (db) {
      return new Promise(function (res, rej) {
        var r = store(db, 'readwrite').delete(id);
        r.onsuccess = function () { res(); };
        r.onerror = function () { rej(r.error); };
      });
    });
  }
  function idbGetAll() {
    return openDB().then(function (db) {
      return new Promise(function (res, rej) {
        var r = store(db, 'readonly').getAll();
        r.onsuccess = function () { res(r.result || []); };
        r.onerror = function () { rej(r.error); };
      });
    });
  }

  function uuid() {
    try { return crypto.randomUUID(); }
    catch (e) { return 'op-' + Date.now() + '-' + Math.random().toString(16).slice(2); }
  }

  function forms() {
    return Array.prototype.slice.call(document.querySelectorAll('form[data-paintops-timeclock]'));
  }

  // ---- UI helpers -------------------------------------------------
  function showPending(form, msg) {
    var box = form.closest('.cs-financial-box') || form.parentNode;
    if (!box) { return; }
    var prev = box.querySelector('[data-paintops-tc-pending]');
    if (prev) { prev.parentNode.removeChild(prev); }
    var el = document.createElement('div');
    el.className = 'paintops-tc-pending';
    el.setAttribute('data-paintops-tc-pending', '1');
    el.textContent = msg;
    box.insertBefore(el, form);
  }

  function setButton(form, mode) {
    var btn = form.querySelector('button[type="submit"]');
    if (!btn) { return; }
    if (mode === 'out') {
      btn.setAttribute('name', 'paintops_job_checkout');
      btn.textContent = 'Check Out';
      btn.disabled = false;
      btn.style.opacity = '';
    } else if (mode === 'done') {
      btn.disabled = true;
      btn.textContent = 'Saved offline';
      btn.style.opacity = '0.6';
    }
  }

  // ---- offline submit interception --------------------------------
  document.addEventListener('submit', function (e) {
    var form = e.target;
    if (!form || !form.getAttribute || form.getAttribute('data-paintops-timeclock') === null) { return; }

    var submitter = e.submitter;
    var action = null;
    if (submitter && submitter.name === 'paintops_job_checkin') { action = 'in'; }
    else if (submitter && submitter.name === 'paintops_job_checkout') { action = 'out'; }
    else if (form.querySelector('[name="paintops_job_checkin"]')) { action = 'in'; }
    else if (form.querySelector('[name="paintops_job_checkout"]')) { action = 'out'; }
    if (!action) { return; }

    // Online: leave the normal POST alone (unchanged live behavior).
    if (navigator.onLine) { return; }

    e.preventDefault();
    var jobId = parseInt(form.getAttribute('data-paintops-timeclock'), 10) || 0;
    if (!jobId) { return; }

    var op = {
      op_id: uuid(),
      job_id: jobId,
      action: action,
      client_ts: Math.floor(Date.now() / 1000),
      created_at: Date.now()
    };
    idbPut(op).then(function () {
      if (action === 'in') {
        showPending(form, 'Checked in and saved on your phone. It will sync automatically when you are back online.');
        setButton(form, 'out');
      } else {
        showPending(form, 'Check-out saved on your phone. It will sync automatically when you are back online.');
        setButton(form, 'done');
      }
    }).catch(function () {
      showPending(form, 'Could not save offline on this device. Please try again when you have signal.');
    });
  }, true);

  // ---- reconcile displayed state from pending ops (offline reloads)
  function reconcile() {
    if (reconciled) { return; }
    var fs = forms();
    if (!fs.length) { return; }
    idbGetAll().then(function (ops) {
      if (!ops.length) { return; }
      reconciled = true;
      fs.forEach(function (form) {
        var jobId = parseInt(form.getAttribute('data-paintops-timeclock'), 10) || 0;
        var mine = ops.filter(function (o) { return o.job_id === jobId; })
                      .sort(function (a, b) { return a.created_at - b.created_at; });
        if (!mine.length) { return; }
        var last = mine[mine.length - 1];
        if (last.action === 'in') {
          setButton(form, 'out');
          showPending(form, 'A check-in is saved on your phone, waiting to sync.');
        } else {
          setButton(form, 'done');
          showPending(form, 'A check-out is saved on your phone, waiting to sync.');
        }
      });
    }).catch(function () {});
  }

  // ---- drain the outbox to the server -----------------------------
  var draining = false;
  function drain() {
    if (draining || !navigator.onLine) { return; }
    draining = true;
    var applied = 0;

    idbGetAll().then(function (ops) {
      ops.sort(function (a, b) { return a.created_at - b.created_at; });

      return ops.reduce(function (chain, op) {
        return chain.then(function (stop) {
          if (stop) { return true; }
          var body = new URLSearchParams();
          body.set('action', 'paintops_timeclock_sync');
          body.set('nonce', CFG.nonce || '');
          body.set('job_id', String(op.job_id));
          body.set('op', op.action);
          body.set('client_ts', String(op.client_ts));

          return fetch(CFG.ajax, { method: 'POST', credentials: 'same-origin', body: body })
            .then(function (res) {
              if (res.ok) { return idbDelete(op.op_id).then(function () { applied++; return false; }); }
              // Terminal client errors: drop so one bad entry can't wedge
              // the whole queue. Server errors: stop and retry next time.
              if (res.status === 400 || res.status === 401 || res.status === 403) {
                return idbDelete(op.op_id).then(function () { return false; });
              }
              return true;
            })
            .catch(function () { return true; });
        });
      }, Promise.resolve(false));
    }).then(function () {
      draining = false;
      // If anything synced and we're looking at a Time Clock, refresh once
      // so the page shows the real server state (logged hours, etc.).
      if (applied > 0 && navigator.onLine && forms().length) {
        idbGetAll().then(function (left) {
          if (!left.length) { window.location.reload(); }
        }).catch(function () {});
      }
    }).catch(function () { draining = false; });
  }

  // ---- boot -------------------------------------------------------
  reconcile();
  drain();
  window.addEventListener('online', drain);
})();
