const buildPostgresEntry = (entry) => {
  const pgEntry = { ...entry };
  Object.keys(pgEntry).forEach((col) => {
    if (pgEntry[col] === undefined) delete pgEntry[col];
  });
  return pgEntry;
};

export const buildPostgresValue = (jsVar) => {
  if (jsVar === null) return "null";
  if (typeof jsVar === "string") return buildPostgresString(jsVar);
  if (Array.isArray(jsVar) && jsVar.length === 0) return "null";
  if (Array.isArray(jsVar) && isTypeArray(jsVar, "string"))
    return buildPostgresStringArray(jsVar);
  return jsVar;
};

const buildPostgresStringArray = (jsonArray) => {
  if (jsonArray.length === 0) return null;
  var pgArray = [...jsonArray];
  var arrayString = "ARRAY [";
  pgArray.forEach((e, i) => (pgArray[i] = `'${e}'`));
  arrayString += pgArray.join(",");
  arrayString += "]";
  return arrayString;
};

const isTypeArray = (jsonArray, type) =>
  jsonArray.every((e) => typeof e === type);

const buildPostgresString = (jsonString) =>
  (jsonString && `'${jsonString.replaceAll("'", "''")}'`) || null;

export const insertQuery = (table, jsEntry) => {
  if (typeof jsEntry !== "object") throw Error("PG Inserts must be objects!");
  const entry = buildPostgresEntry(jsEntry);
  const cols = Object.keys(entry);
  cols.forEach((col, i) => {
    entry[col] = buildPostgresValue(entry[col]);
    cols[i] = `"${col}"`;
  });
  var query = `INSERT INTO ${table}(${cols.join(",")})\n`;
  query += `VALUES(${Object.values(entry).join(",")})`;
  return query;
};

export const deleteQuery = (table, jsEntry) => {
  if (typeof jsEntry !== "object")
    throw Error("PG Delete conditionals must be an object!");
  const entry = buildPostgresEntry(jsEntry);
  const cols = Object.keys(entry);
  const conditionals = [];
  for (var col of cols) {
    entry[col] = buildPostgresValue(entry[col]);
    conditionals.push(`x.${col}=${entry[col]}`);
  }
  return `DELETE FROM ${table} x WHERE ${conditionals.join(" AND ")}`;
};
export const onConflictUpdate = (conflicts, updates) => {
  if (!Array.isArray(conflicts)) throw Error("PG Conflicts must be an array!");
  if (typeof updates !== "object") throw Error("PG Updates must be objects!");
  const entry = buildPostgresEntry(updates);
  var query = `ON CONFLICT (${conflicts.join(",")}) DO UPDATE SET\n`;
  const cols = Object.keys(entry);
  for (var col of cols) {
    entry[col] = buildPostgresValue(entry[col]);
  }
  query += cols.map((c) => `${c}=${entry[c]}`).join(",");
  return query;
};
export const clearTableQuery = (table) => {
  return `TRUNCATE ${table}`;
};

export const selectWhereQuery = (table, jsEntry, joinWith) => {
  if (typeof jsEntry !== "object") throw Error("PG Where must be an object!");
  const where = buildPostgresEntry(jsEntry);
  const cols = Object.keys(where);
  var query = `SELECT * FROM ${table} AS x WHERE\n`;
  for (var col of cols) {
    where[col] = buildPostgresValue(where[col]);
  }
  return (query += cols.map((c) => `x.${c}=${where[c]}`).join(joinWith));
};
export const updateWhereQuery = (table, updates, wheres, joinWith) => {
  if (typeof updates !== "object") throw Error("PG Updates must be an object!");
  if (typeof wheres !== "object") throw Error("PG Wheres must be an object!");
  const update = buildPostgresEntry(updates);
  const where = buildPostgresEntry(wheres);
  const updateCols = Object.keys(update);
  const whereCols = Object.keys(where);
  var query = `UPDATE ${table}\n`;
  var updateQuery = updateCols
    .map((c) => `${c} = ${buildPostgresValue(update[c])}`)
    .join(",");
  var whereQuery = whereCols
    .map((c) => `${c} = ${buildPostgresValue(where[c])}`)
    .join(joinWith);
  return (query += `SET ${updateQuery} WHERE ${whereQuery}`);
};
export const updateWhereAnyQuery = (table, updates, wheres) =>
  updateWhereQuery(table, updates, wheres, " OR ");
export const updateWhereAllQuery = (table, updates, wheres) =>
  updateWhereQuery(table, updates, wheres, " AND ");
export const selectWhereAnyQuery = (table, where) =>
  selectWhereQuery(table, where, " OR ");
export const selectWhereAllQuery = (table, where) =>
  selectWhereQuery(table, where, " AND ");

export default {
  selectWhereAnyQuery,
  selectWhereAllQuery,
  updateWhereAnyQuery,
  updateWhereAllQuery,
  insertQuery,
  deleteQuery,
  buildPostgresValue,
  onConflictUpdate,
  clearTableQuery,
};
