-- plxjs regression tests
CREATE EXTENSION IF NOT EXISTS plx;
SET client_min_messages = warning;

-- scalar return
CREATE FUNCTION js_add(a int, b int) RETURNS int LANGUAGE plxjs AS $$
return a + b * 2;
$$;
SELECT js_add(3, 4);

-- let, if / else if / else
CREATE FUNCTION js_grade(score int) RETURNS text LANGUAGE plxjs AS $$
let grade = "F";
if (score >= 90) { grade = "A"; }
else if (score >= 80) { grade = "B"; }
else { grade = "F"; }
return grade;
$$;
SELECT js_grade(95), js_grade(85), js_grade(70);

-- C-style for with let
CREATE FUNCTION js_sum(n int) RETURNS int LANGUAGE plxjs AS $$
let total = 0;
for (let i = 1; i <= n; i++) {
  total = total + i;
}
return total;
$$;
SELECT js_sum(10);

-- template literal interpolation
CREATE FUNCTION js_greet(nm text) RETURNS text LANGUAGE plxjs AS $$
return `Hello, ${nm}!`;
$$;
SELECT js_greet('World');

CREATE FUNCTION js_interp(nm text, n int) RETURNS text LANGUAGE plxjs AS $$
return `user ${nm} has ${n} items`;
$$;
SELECT js_interp('bob', 3);

-- while
CREATE FUNCTION js_countdown(n int) RETURNS int LANGUAGE plxjs AS $$
let c = 0;
while (n > 0) { c = c + n; n = n - 1; }
return c;
$$;
SELECT js_countdown(5);

-- for-of over a query
CREATE TABLE js_orders(grp int, amount bigint);
INSERT INTO js_orders VALUES (1,10),(1,20),(1,30),(2,5);
CREATE FUNCTION js_grp_total(g int) RETURNS bigint LANGUAGE plxjs AS $$
let total = 0 /*:: bigint */;
for (const row of query(`SELECT amount FROM js_orders WHERE grp = ${g}`)) {
  total = total + row.amount;
}
return total;
$$;
SELECT js_grp_total(1), js_grp_total(2);

-- fetch_one
CREATE TABLE js_users(id int, name text);
INSERT INTO js_users VALUES (1,'Alice'),(2,'Bob');
CREATE FUNCTION js_name(uid int) RETURNS text LANGUAGE plxjs AS $$
let u = fetch_one(`SELECT id, name FROM js_users WHERE id = ${uid}`);
return u.name;
$$;
SELECT js_name(2);

-- SETOF via return_next
CREATE FUNCTION js_squares(n int) RETURNS SETOF int LANGUAGE plxjs AS $$
for (let i = 1; i <= n; i++) { return_next(i * i); }
return;
$$;
SELECT * FROM js_squares(3);

-- try / catch
CREATE FUNCTION js_safediv(d int) RETURNS int LANGUAGE plxjs AS $$
try {
  return 100 / d;
} catch (e) {
  raise("notice", `caught: ${e.message}`);
  return -1;
}
$$;
SELECT js_safediv(4), js_safediv(0);

-- throw
CREATE FUNCTION js_checkpos(v int) RETURNS int LANGUAGE plxjs AS $$
if (v < 0) { throw new Error(`negative: ${v}`); }
return v;
$$;
SELECT js_checkpos(7);

-- DO block (body is JavaScript)
DO LANGUAGE plxjs $$
for (let i = 1; i <= 2; i++) { raise("warning", `row ${i}`); }
$$;

-- switch (with stacked cases and default)
CREATE FUNCTION js_switch(n int) RETURNS text LANGUAGE plxjs AS $$
switch (n) {
  case 1: return "one";
  case 2: case 3: return "few";
  default: return "many";
}
$$;
SELECT js_switch(1), js_switch(2), js_switch(9);

-- assert
CREATE FUNCTION js_assert(n int) RETURNS int LANGUAGE plxjs AS $$
assert(n > 0, "must be positive");
return n;
$$;
SELECT js_assert(5);

-- FOREACH over an array (for..of)
CREATE FUNCTION js_sumarr(a int[]) RETURNS int LANGUAGE plxjs AS $$
let total = 0 /*:: int */;
let v /*:: int */;
for (const v of a) { total = total + v; }
return total;
$$;
SELECT js_sumarr(ARRAY[1,2,3,4]);

-- cursors
CREATE TABLE js_nums(v int);
INSERT INTO js_nums SELECT generate_series(1,4);
CREATE FUNCTION js_cursor_sum() RETURNS int LANGUAGE plxjs AS $$
let total = 0 /*:: int */;
let c = open_cursor(`SELECT v FROM js_nums ORDER BY v`);
let row = fetch_from(c);
while (found()) {
  total = total + row.v;
  row = fetch_from(c);
}
close_cursor(c);
return total;
$$;
SELECT js_cursor_sum();

-- labeled loop
CREATE FUNCTION js_labeled(n int) RETURNS int LANGUAGE plxjs AS $$
let count = 0 /*:: int */;
outer: for (let i = 1; i <= n; i++) {
  for (let j = 1; j <= n; j++) {
    count = count + 1;
    if (i + j >= 4) { break outer; }
  }
}
return count;
$$;
SELECT js_labeled(5);

-- non-decimal integer literals (hex/binary/octal, with '_' groups) convert to
-- decimal so the generated plpgsql is portable to PG13-15 (no 0x/0o/0b there)
CREATE FUNCTION js_hex()    RETURNS int LANGUAGE plxjs AS $$ return 0xFF; $$;
CREATE FUNCTION js_bin()    RETURNS int LANGUAGE plxjs AS $$ return 0b1010; $$;
CREATE FUNCTION js_oct()    RETURNS int LANGUAGE plxjs AS $$ return 0o17; $$;
CREATE FUNCTION js_hex_us() RETURNS int LANGUAGE plxjs AS $$ return 0xFF_FF; $$;
SELECT js_hex() AS hex, js_bin() AS bin, js_oct() AS oct, js_hex_us() AS hex_us;

-- float and exponent literals are unaffected
CREATE FUNCTION js_flt() RETURNS numeric LANGUAGE plxjs AS $$ return 3.14; $$;
CREATE FUNCTION js_exp() RETURNS numeric LANGUAGE plxjs AS $$ return 1.5e3; $$;
SELECT js_flt() AS flt, js_exp() AS exp;
