CREATE OR REPLACE PROCEDURE public.add_nodes(IN p_user_id integer, IN p_client_uuid uuid, IN p_nodes jsonb)
 LANGUAGE plpgsql
AS $procedure$

DECLARE
	node_data         jsonb;
	node_updated      timestamptz;
	db_updated        timestamptz;
	db_uuid           uuid;
	db_client         uuid;
	db_history_uuid   uuid;
	node_uuid         uuid;
	node_parent_uuid  uuid;
	node_history_uuid uuid;

BEGIN
	FOR node_data IN SELECT * FROM jsonb_array_elements(p_nodes)
	LOOP
		node_uuid         = (node_data->>'UUID')::uuid;
		node_history_uuid = (node_data->>'HistoryUUID')::uuid;
		node_updated      = (node_data->>'Updated')::timestamptz;



		-- Frontend is using an all-zero UUID to define the root node.
		-- Database is using NULL.
		IF node_data->>'ParentUUID' = '00000000-0000-0000-0000-000000000000' OR node_data->>'ParentUUID' = '' THEN
			node_parent_uuid = NULL;
		ELSE
			node_parent_uuid = (node_data->>'ParentUUID')::uuid;
		END IF;

		-- Safeguard against being your own parent.
		IF node_uuid = node_parent_uuid THEN
			RAISE EXCEPTION 'Node UUID is same as node parent UUID.' USING ERRCODE = 'XPRNT';
		END IF;


		-- Every jode has a new history UUID to keep the history entry uniquely identifiable
		-- across clients. A history entry could potentially be sent again, but should be
		-- safe to ignore as every change to a node should have a new history UUID.
		--
		-- The current node is also stored as history.
		INSERT INTO node_history(
			user_id, "uuid", "history_uuid", parents, created, updated,
			"name", "content", "content_encrypted",
			client
		)
		VALUES(
			p_user_id, -- combined key
			node_uuid, -- combined key
			node_history_uuid, -- combined key
			(jsonb_populate_record(null::json_ancestor_array, node_data))."Ancestors",
			COALESCE((node_data->>'Created')::timestamptz, NOW()),
			COALESCE((node_data->>'Updated')::timestamptz, NOW()),
			(node_data->>'Name')::varchar,
			(node_data->>'Content')::text,
			'', /* content_encrypted */
			p_client_uuid
		)
		ON CONFLICT ("user_id", "uuid", "history_uuid")
		DO NOTHING;



		-- Retrieve the current modified timestamp for this node from the database.
		SELECT
			uuid, updated, client
		INTO
			db_uuid, db_updated, db_client
		FROM public."node"
		WHERE
			user_id = p_user_id AND
			uuid::uuid = node_uuid::uuid;



		-- Is the node not in database? It needs to be created.
		IF db_uuid IS NULL THEN
			RAISE NOTICE '01 New node %', node_uuid;

			INSERT INTO public."node" (
				user_id, "uuid", parent_uuid, created, updated,
				"name", "content", "content_encrypted",
				client
			)
			VALUES(
				p_user_id,
				node_uuid,
				node_parent_uuid,
				COALESCE((node_data->>'Created')::timestamptz, NOW()),
				COALESCE((node_data->>'Updated')::timestamptz, NOW()),
				(node_data->>'Name')::varchar,
				(node_data->>'Content')::text,
				'', /* content_encrypted */
				p_client_uuid
			);

			CONTINUE;

		END IF;



		-- Update the public node as well if it was older than incoming node.
		IF node_updated > db_updated THEN
			UPDATE public."node"
			SET
				updated = (node_data->>'Updated')::timestamptz,
				updated_seq = nextval('node_updates'),
				parent_uuid = node_parent_uuid,
				name = (node_data->>'Name')::varchar,
				content = (node_data->>'Content')::text,
				client = p_client_uuid
			WHERE
				user_id = p_user_id AND
				uuid::uuid = node_uuid::uuid;
		END IF;

	END LOOP;
END
$procedure$
;
