SELECT _snowflake_template(
  $sql$
    CREATE OR REPLACE FUNCTION snowflake_gen(
      seq_name regclass = NULL,
      ts_start bigint = {CONST_SNOWFLAKE_TS_START},
      ts_bits integer = {CONST_SNOWFLAKE_TS_BITS},
      cluster_no_bits integer = {CONST_SNOWFLAKE_CLUSTER_NO_BITS},
      island_no_bits integer = {CONST_SNOWFLAKE_ISLAND_NO_BITS},
      seq_bits integer = {CONST_SNOWFLAKE_SEQ_BITS}
    ) RETURNS bigint
    LANGUAGE plpgsql
    SET search_path FROM CURRENT
    AS $$
    DECLARE
      cluster_no integer;
      island_no integer;
      seq_no bigint;
      ts double precision;
      id bigint;
    BEGIN
      cluster_no := {I:current_schema}.snowflake_cluster_no();
      island_no := {I:current_schema}.snowflake_island_no();
      IF ts_bits + cluster_no_bits + island_no_bits + seq_bits <> 64 THEN
        RAISE EXCEPTION
          'ts_bits=% + cluster_no_bits=% + island_no_bits=% + seq_bits=% must be 64 exactly',
          ts_bits, cluster_no_bits, island_no_bits, seq_bits;
      ELSIF cluster_no_bits > 0 AND (cluster_no < 0 OR cluster_no >= (1 << cluster_no_bits)) THEN
        RAISE EXCEPTION 'snowflake_cluster_no() returned % which does not fit in % bits', cluster_no, cluster_no_bits;
      ELSIF island_no_bits > 0 AND (island_no < 0 OR island_no >= (1 << island_no_bits)) THEN
        RAISE EXCEPTION 'snowflake_island_no() returned % which does not fit in % bits', island_no, island_no_bits;
      END IF;
      IF seq_name IS NULL THEN
        seq_name := (quote_ident({current_schema}) || '.snowflake_seq')::regclass;
      END IF;
      seq_no := nextval(seq_name);
      -- Full milliseconds timestamp is 40 bits, but we only want ts_bits of the
      -- HIGHEST bits from it, so shifting right.
      id := (EXTRACT(EPOCH FROM clock_timestamp()) * 1000 - ts_start)::bigint >> (40 - ts_bits);
      id := (id << cluster_no_bits) | cluster_no;
      id := (id << island_no_bits) | island_no;
      id := (id << seq_bits) | (seq_no & ((1 << seq_bits) - 1));
      RETURN id;
    END
    $$;

    COMMENT ON FUNCTION snowflake_gen(regclass, bigint, integer, integer, integer, integer) IS
      'Generate Snowflake ID Standard compatible ids with optional cluster number: '
      'https://en.wikipedia.org/wiki/Snowflake_ID';
  $sql$,
  'CONST_SNOWFLAKE_TS_START', :'CONST_SNOWFLAKE_TS_START',
  'CONST_SNOWFLAKE_TS_BITS', :'CONST_SNOWFLAKE_TS_BITS',
  'CONST_SNOWFLAKE_CLUSTER_NO_BITS', :'CONST_SNOWFLAKE_CLUSTER_NO_BITS',
  'CONST_SNOWFLAKE_ISLAND_NO_BITS', :'CONST_SNOWFLAKE_ISLAND_NO_BITS',
  'CONST_SNOWFLAKE_SEQ_BITS', :'CONST_SNOWFLAKE_SEQ_BITS',
  'current_schema', current_schema
) \gexec

