// tc-store.jsx — localStorage persistence for profile + saved timecards.

const LS_PROFILE = 'btl_tc_profile_v2';
const LS_SIGNATURE = 'btl_tc_signature_v1';   // stored separately (large data URL)
const LS_CARDS = 'btl_tc_cards_v2';
const LS_RECENTS = 'btl_tc_recents_v2';
const LS_KITS = 'btl_kit_forms_v1';

// EVERY key the app persists. Backup includes all of them so nothing is missed.
// If you add a new localStorage key anywhere, add it here too.
const BACKUP_KEYS = [LS_PROFILE, LS_SIGNATURE, LS_CARDS, LS_RECENTS, LS_KITS];

// Profile = the stuff that's the same every week.
const DEFAULT_PROFILE = {
  fullName: '',
  occupation: '',
  ssnLast4: '',
  initials: '',         // for output file names
  perDiemRate: '',      // default $/day
  fileNamePatternTC: '',   // time-card output file-name template ('' → default)
  fileNamePatternKit: '',  // kit output file-name template ('' → default)
  signature: '',        // PNG data URL (drawn or uploaded)
};

// Per-timecard fields = the stuff WE fill out each week.
const DEFAULT_CARD = {
  projectTitle: '',
  initials: '',         // overrides profile initials for the file name
  weekEnding: '',       // ISO YYYY-MM-DD (anchor)
  workCity: '',
  workState: '',
  perDiemRate: '',      // overrides profile default if set
  perDiemDays: '',      // manual override of per-diem day count ('' = auto)
  boxRental: '',        // → NO WITHHOLDING / 1099 (EQR) box
  pasteText: '',
  autoMeal: true,       // auto-fill 1st meal (out=call+6h, in=call+6h30m)
  annotations: [],      // free-text notes placed anywhere on the form
};

// Per-kit-rental fields = what WE fill on the Box Rental Form.
const DEFAULT_KIT = {
  company: '',
  kitType: '',          // e.g. DRONE → file name HSK_DRONEKIT_WE_...
  initials: '',         // overrides profile initials for the file name
  rentalRate: '',
  rentalPer: 'week',    // 'day' | 'week'
  inventory: '',        // one item per line (up to 8 lines)
  invCheck: '',         // '' | 'onfile' | 'attached'  (unchecked by default)
  weekEnding: '',       // ISO YYYY-MM-DD
  date: '',             // ISO YYYY-MM-DD (signature date)
  annotations: [],      // free-text notes placed anywhere on the form
};

function loadJSON(key, fallback) {
  try { const raw = localStorage.getItem(key); return raw ? JSON.parse(raw) : fallback; }
  catch (e) { return fallback; }
}
function saveJSON(key, val) {
  try { localStorage.setItem(key, JSON.stringify(val)); return true; }
  catch (e) { console.warn('localStorage write failed for', key, e); return false; }
}

const Store = {
  // Profile is stored in two keys: the small fields, and the (large) signature
  // data URL on its own. Isolating the signature means a quota problem with it
  // can never wipe the rest of the profile, and vice-versa.
  getProfile() {
    const raw = loadJSON(LS_PROFILE, {});
    const base = { ...DEFAULT_PROFILE, ...raw };
    let sig = '';
    try { sig = localStorage.getItem(LS_SIGNATURE) || ''; } catch (e) {}
    // migrate: older builds stored the signature inside the profile blob
    if (!sig && raw.signature) { sig = raw.signature; Store.saveSignature(sig); }
    base.signature = sig;
    // migrate: older builds had a single fileNamePattern for both forms
    if (raw.fileNamePattern && !raw.fileNamePatternTC && !raw.fileNamePatternKit) {
      base.fileNamePatternTC = raw.fileNamePattern;
      base.fileNamePatternKit = raw.fileNamePattern;
    }
    return base;
  },
  saveProfile(p) {
    // NB: avoid object-rest destructuring here — Babel compiles it to a shared
    // global `_excluded` that collides across our multiple <script> files.
    const rest = Object.assign({}, p || {});
    const signature = rest.signature;
    delete rest.signature;
    saveJSON(LS_PROFILE, rest);
    return Store.saveSignature(signature || '');
  },
  saveSignature(sig) {
    try {
      if (sig) localStorage.setItem(LS_SIGNATURE, sig);
      else localStorage.removeItem(LS_SIGNATURE);
      return true;
    } catch (e) { console.warn('signature too large to save', e); return false; }
  },

  getCards() { return loadJSON(LS_CARDS, []); },
  saveCards(c) { saveJSON(LS_CARDS, c); },
  upsertCard(card) {
    const cards = Store.getCards();
    const i = cards.findIndex(c => c.id === card.id);
    if (i >= 0) cards[i] = { folderId: cards[i].folderId, ...card }; else cards.unshift(card);
    Store.saveCards(cards);
    return cards;
  },
  deleteCard(id) {
    const cards = Store.getCards().filter(c => c.id !== id);
    Store.saveCards(cards);
    return cards;
  },

  getRecents() { return loadJSON(LS_RECENTS, []); },
  addRecent(city, state) {
    if (!city && !state) return Store.getRecents();
    let r = Store.getRecents().filter(x => !(x.city === city && x.state === state));
    r.unshift({ city, state });
    r = r.slice(0, 6);
    saveJSON(LS_RECENTS, r);
    return r;
  },

  getKits() { return loadJSON(LS_KITS, []); },
  saveKits(k) { saveJSON(LS_KITS, k); },
  upsertKit(kit) {
    const kits = Store.getKits();
    const i = kits.findIndex(k => k.id === kit.id);
    if (i >= 0) kits[i] = { folderId: kits[i].folderId, ...kit }; else kits.unshift(kit);
    Store.saveKits(kits);
    return kits;
  },
  deleteKit(id) {
    const kits = Store.getKits().filter(k => k.id !== id);
    Store.saveKits(kits);
    return kits;
  },
};

function newCardId() { return 'tc_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }
function newKitId() { return 'kit_' + Date.now().toString(36) + Math.random().toString(36).slice(2, 6); }

// ── Backup & Restore ─────────────────────────────────────────────────────────
const BACKUP_APP_ID = 'timecard-genny';
const BACKUP_VERSION = 1;

// Build the backup object: every key's RAW string value, exactly as stored.
function buildBackup() {
  const data = {};
  for (const key of BACKUP_KEYS) {
    let v = null;
    try { v = localStorage.getItem(key); } catch (e) {}
    if (v != null) data[key] = v;          // raw string, not re-parsed
  }
  return {
    app: BACKUP_APP_ID,
    version: BACKUP_VERSION,
    exportedAt: new Date().toISOString(),
    data,
  };
}

// BACKUP_[INITIALS]_TCGENNY_BACKUP_YYYYMMDD.json
function backupFilename() {
  let initials = 'TCGENNY';
  try {
    const p = loadJSON(LS_PROFILE, {});
    if (p && p.initials) initials = String(p.initials).toUpperCase().replace(/[^A-Z0-9]/g, '') || 'TCGENNY';
  } catch (e) {}
  const d = new Date();
  const ymd = `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}`;
  return `${initials}_TCGENNY_BACKUP_${ymd}.json`;
}

// Download the backup as a single JSON file.
function exportBackup() {
  const blob = new Blob([JSON.stringify(buildBackup(), null, 2)], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = backupFilename();
  document.body.appendChild(a); a.click(); a.remove();
  setTimeout(() => URL.revokeObjectURL(url), 1000);
  return a.download;
}

// Validate a parsed backup object. Returns { ok, error, keys }.
function validateBackup(obj) {
  if (!obj || typeof obj !== 'object') return { ok: false, error: 'Not a valid backup file.' };
  if (obj.app !== BACKUP_APP_ID) return { ok: false, error: 'This file is not a TIMECARD GENNY backup.' };
  if (!obj.data || typeof obj.data !== 'object') return { ok: false, error: 'Backup file has no data.' };
  const keys = Object.keys(obj.data);
  if (keys.length === 0) return { ok: false, error: 'Backup file is empty.' };
  // every value must be a string (raw localStorage form)
  for (const k of keys) {
    if (typeof obj.data[k] !== 'string') return { ok: false, error: 'Backup data is malformed (expected raw strings).' };
  }
  return { ok: true, keys };
}

// Write every key/value back EXACTLY as stored. Clears our keys first so a
// restore from a backup that omits a key doesn't leave stale data behind.
// Only called after validateBackup() passes, so it never partially applies a bad file.
function applyBackup(obj) {
  const v = validateBackup(obj);
  if (!v.ok) return v;
  try {
    for (const key of BACKUP_KEYS) {
      try { localStorage.removeItem(key); } catch (e) {}
    }
    for (const [key, value] of Object.entries(obj.data)) {
      localStorage.setItem(key, value);    // raw string straight back in
    }
    return { ok: true };
  } catch (e) {
    return { ok: false, error: 'Could not write the backup (storage may be full).' };
  }
}

Object.assign(window, { buildBackup, backupFilename, exportBackup, validateBackup, applyBackup, BACKUP_KEYS });

Object.assign(window, { Store, DEFAULT_PROFILE, DEFAULT_CARD, DEFAULT_KIT, newCardId, newKitId });
