CREATE OR REPLACE FUNCTION create_schema(
    schema_name NAME
)
    RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
$BODY$
BEGIN
    EXECUTE format(
        $$
            CREATE SCHEMA %I
        $$, schema_name);
END
$BODY$;

CREATE OR REPLACE FUNCTION create_cluster_table(
    schema_name NAME,
    table_name  NAME
)
    RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
$BODY$
BEGIN
    EXECUTE format(
        $$
            CREATE TABLE IF NOT EXISTS %I.%I (
                time BIGINT NOT NULL
            )
        $$, schema_name, table_name);
END
$BODY$;

CREATE OR REPLACE FUNCTION create_cluster_distinct_table(
    schema_name NAME,
    table_name  NAME
)
    RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
$BODY$
BEGIN
    EXECUTE format(
        $$
            CREATE TABLE IF NOT EXISTS %1$I.%2$I (
                field TEXT,
                value TEXT,
                last_time_approx BIGINT,
                PRIMARY KEY(field, value)
            );
        $$, schema_name, table_name);
END
$BODY$;

CREATE OR REPLACE FUNCTION create_local_distinct_table(
    schema_name        NAME,
    table_name         NAME,
    cluster_table_name NAME
)
    RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
$BODY$
BEGIN
    EXECUTE format(
        $$
            CREATE TABLE IF NOT EXISTS %1$I.%2$I (PRIMARY KEY(field, value))
            INHERITS(%1$I.%3$I);

        $$, schema_name, table_name, cluster_table_name);
END
$BODY$;

CREATE OR REPLACE FUNCTION create_remote_distinct_table(
    schema_name        NAME,
    local_table_name   NAME,
    remote_table_name  NAME,
    cluster_table_name NAME,
    server_name        NAME
)
    RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
$BODY$
BEGIN
    EXECUTE format(
        $$
            CREATE FOREIGN TABLE IF NOT EXISTS %1$I.%2$I () INHERITS(%1$I.%3$I) SERVER %4$I
            OPTIONS (schema_name %1$L, table_name %5$L)
        $$,
        schema_name, remote_table_name, cluster_table_name, server_name, local_table_name);
END
$BODY$;

CREATE OR REPLACE FUNCTION create_master_table(
    schema_name        NAME,
    table_name         NAME,
    cluster_table_name NAME
)
    RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
$BODY$
BEGIN
    EXECUTE format(
        $$
            CREATE TABLE IF NOT EXISTS %1$I.%2$I () INHERITS(%1$I.%3$I)
        $$,
        schema_name, table_name, cluster_table_name);
END
$BODY$;

CREATE OR REPLACE FUNCTION create_remote_table(
    schema_name        NAME,
    remote_table_name  NAME,
    master_table_name  NAME,
    cluster_table_name NAME,
    server_name        NAME
)
    RETURNS VOID LANGUAGE PLPGSQL VOLATILE AS
$BODY$
BEGIN
    EXECUTE format(
        $$
            CREATE FOREIGN TABLE IF NOT EXISTS %1$I.%2$I () INHERITS(%1$I.%3$I) SERVER %4$I OPTIONS (schema_name %1$L, table_name %5$L)
        $$,
        schema_name, remote_table_name, cluster_table_name, server_name, master_table_name);
END
$BODY$;

CREATE OR REPLACE FUNCTION _sysinternal.on_create_namespace()
    RETURNS TRIGGER LANGUAGE PLPGSQL AS
$BODY$
DECLARE
    remote_node node;
BEGIN
    IF TG_OP <> 'INSERT' THEN
        RAISE EXCEPTION 'Only inserts supported on namespace table'
        USING ERRCODE = 'IO101';
    END IF;

    IF NEW.schema_name = 'public' THEN
        RAISE EXCEPTION 'public is not an allowed namespace name'
        USING ERRCODE = 'IO104';
    END IF;

    IF position('_' IN NEW.schema_name) = 1 THEN
        RAISE EXCEPTION 'Namespace cannot begin with an underscore (_)'
        USING ERRCODE = 'IO104';
    END IF;

    PERFORM create_schema(NEW.schema_name);
    PERFORM create_cluster_table(NEW.schema_name, NEW.cluster_table_name);
    PERFORM create_cluster_distinct_table(NEW.schema_name, NEW.cluster_distinct_table_name);
    RETURN NEW;
END
$BODY$
SET SEARCH_PATH = 'public';

BEGIN;
DROP TRIGGER IF EXISTS trigger_on_create_namespace
ON namespace;
CREATE TRIGGER trigger_on_create_namespace AFTER INSERT OR UPDATE OR DELETE ON namespace
FOR EACH ROW EXECUTE PROCEDURE _sysinternal.on_create_namespace();
COMMIT;


CREATE OR REPLACE FUNCTION _sysinternal.on_create_namespace_node()
    RETURNS TRIGGER LANGUAGE PLPGSQL AS
$BODY$
DECLARE
    namespace_row namespace;
    node_row      node;
BEGIN
    IF TG_OP <> 'INSERT' THEN
        RAISE EXCEPTION 'Only inserts supported on namespace table'
        USING ERRCODE = 'IO101';
    END IF;

    SELECT *
    INTO STRICT namespace_row
    FROM namespace AS ns
    WHERE ns.name = NEW.namespace_name;

    IF NEW.database_name = current_database() THEN
        PERFORM create_master_table(namespace_row.schema_name, NEW.master_table_name, namespace_row.cluster_table_name);
        PERFORM create_local_distinct_table(namespace_row.schema_name, NEW.distinct_local_table_name,
                                            namespace_row.cluster_distinct_table_name);
    ELSE
        SELECT *
        INTO STRICT node_row
        FROM node AS n
        WHERE n.database_name = NEW.database_name;

        PERFORM create_remote_table(namespace_row.schema_name, NEW.remote_table_name, NEW.master_table_name,
                                    namespace_row.cluster_table_name,
                                    node_row.server_name);
        PERFORM create_remote_distinct_table(namespace_row.schema_name, NEW.distinct_local_table_name,
                                             NEW.distinct_remote_table_name,
                                             namespace_row.cluster_distinct_table_name,
                                             node_row.server_name);
    END IF;
    RETURN NEW;
END
$BODY$
SET SEARCH_PATH = 'public';

BEGIN;
DROP TRIGGER IF EXISTS trigger_on_create_namespace_node
ON namespace_node;
CREATE TRIGGER trigger_on_create_namespace_node AFTER INSERT OR UPDATE OR DELETE ON namespace_node
FOR EACH ROW EXECUTE PROCEDURE _sysinternal.on_create_namespace_node();
COMMIT;
