function db_() {
  const id = PropertiesService.getScriptProperties().getProperty('DB_SPREADSHEET_ID');
  if (!id) throw new Error('Databáze není inicializovaná. Spusť setupSystem().');
  return SpreadsheetApp.openById(id);
}

function sh_(name) {
  const s = db_().getSheetByName(name);
  if (!s) throw new Error('Chybí list ' + name);
  return s;
}

function rows_(name) {
  const s = sh_(name);
  const values = s.getDataRange().getValues();
  if (values.length < 2) return [];
  const headers = values[0].map(String);
  return values.slice(1).filter(r => r.some(v => v !== '')).map(r => {
    const o = {};
    headers.forEach((h, i) => o[h] = r[i]);
    return o;
  });
}

function append_(name, obj) {
  const s = sh_(name);
  const headers = s.getRange(1,1,1,s.getLastColumn()).getValues()[0];
  s.appendRow(headers.map(h => obj[h] !== undefined ? obj[h] : ''));
  return obj;
}

function updateBy_(name, key, value, patch) {
  const s = sh_(name);
  const data = s.getDataRange().getValues();
  if (!data.length) return false;
  const headers = data[0].map(String);
  const keyCol = headers.indexOf(key);
  if (keyCol < 0) throw new Error('Sloupec ' + key + ' neexistuje v ' + name);
  const row = data.slice(1).findIndex(r => String(r[keyCol]) === String(value));
  if (row < 0) return false;
  Object.keys(patch).forEach(k => {
    const c = headers.indexOf(k);
    if (c >= 0) s.getRange(row + 2, c + 1).setValue(patch[k]);
  });
  return true;
}

function findOne_(name, predicate) {
  return rows_(name).find(predicate) || null;
}

function uuid_(prefix) {
  return (prefix || 'ID') + '_' + Utilities.getUuid().replace(/-/g,'').slice(0,16).toUpperCase();
}

function now_() { return new Date(); }

function isoDate_(d) {
  return Utilities.formatDate(new Date(d), CFG.TZ, 'yyyy-MM-dd');
}

function period_(d) {
  return Utilities.formatDate(new Date(d), CFG.TZ, 'yyyy-MM');
}

function emailNorm_(email) {
  return String(email || '').trim().toLowerCase();
}

function json_(v) {
  try { return JSON.stringify(v); } catch(e) { return String(v); }
}