CREATE TYPE auth_method AS ENUM (
	'OAUTH_LEGACY',
	'OAUTH2',
	'COOKIE',
	'INTERNAL',
	'WEBHOOK'
);

CREATE TYPE ticket_webhook_event AS ENUM (
	'TICKET_UPDATE',
	'EVENT_CREATED',
	'TICKET_DELETED'
);

CREATE TYPE tracker_webhook_event AS ENUM (
	'TRACKER_UPDATE',
	'TRACKER_DELETED',
	'TICKET_CREATED',
	'TICKET_UPDATE',
	'LABEL_CREATED',
	'LABEL_UPDATE',
	'LABEL_DELETED',
	'EVENT_CREATED',
	'TICKET_DELETED'
);

CREATE TYPE visibility AS ENUM (
	'PUBLIC',
	'UNLISTED',
	'PRIVATE'
);

CREATE TYPE webhook_event AS ENUM (
	'TRACKER_CREATED',
	'TRACKER_UPDATE',
	'TRACKER_DELETED',
	'TICKET_CREATED'
);

CREATE TABLE "user" (
	id integer PRIMARY KEY,
	username character varying(256) UNIQUE,
	created timestamp without time zone NOT NULL,
	updated timestamp without time zone NOT NULL,
	oauth_token character varying(256),
	oauth_token_expires timestamp without time zone,
	oauth_token_scopes character varying,
	email character varying(256) NOT NULL,
	user_type character varying DEFAULT 'active_non_paying'::character varying NOT NULL,
	url character varying(256),
	location character varying(256),
	bio character varying(4096),
	oauth_revocation_token character varying(256),
	suspension_notice character varying(4096),
	notify_self boolean DEFAULT false NOT NULL
);

CREATE INDEX ix_user_username ON "user" USING btree (username);

CREATE TABLE participant (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	participant_type character varying NOT NULL,
	user_id integer UNIQUE REFERENCES "user"(id) ON DELETE CASCADE,
	email character varying UNIQUE,
	email_name character varying,
	external_id character varying UNIQUE,
	external_url character varying
);

CREATE TABLE tracker (
	id serial PRIMARY KEY,
	owner_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	created timestamp without time zone NOT NULL,
	updated timestamp without time zone NOT NULL,
	name character varying(1024),
	description character varying(8192),
	default_access integer DEFAULT 7 NOT NULL,
	next_ticket_id integer DEFAULT 1 NOT NULL,
	import_in_progress boolean DEFAULT false NOT NULL,
	visibility visibility NOT NULL,
	CONSTRAINT tracker_owner_id_name_unique UNIQUE (owner_id, name)
);

CREATE TABLE user_access (
	id serial PRIMARY KEY,
	tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	permissions integer NOT NULL,
	created timestamp without time zone NOT NULL,
	CONSTRAINT idx_useraccess_tracker_user_unique UNIQUE (tracker_id, user_id)
);

CREATE TABLE label (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	updated timestamp without time zone NOT NULL,
	tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
	name text NOT NULL,
	color text NOT NULL,
	text_color text NOT NULL,
	CONSTRAINT idx_tracker_name_unique UNIQUE (tracker_id, name)
);

CREATE TABLE ticket (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	updated timestamp without time zone NOT NULL,
	tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
	dupe_of_id integer REFERENCES ticket(id) ON DELETE SET NULL,
	title character varying(2048) NOT NULL,
	description character varying(16384),
	status integer DEFAULT 0 NOT NULL,
	resolution integer DEFAULT 0 NOT NULL,
	scoped_id integer NOT NULL,
	submitter_id integer NOT NULL REFERENCES participant(id) ON DELETE CASCADE,
	authenticity integer DEFAULT 0 NOT NULL,
	comment_count integer DEFAULT 0 NOT NULL,
	CONSTRAINT uq_ticket_scoped_id_tracker_id UNIQUE (scoped_id, tracker_id),
	CONSTRAINT uq_ticket_tracker_id_scoped_id UNIQUE (tracker_id, scoped_id)
);

CREATE INDEX ix_ticket_comment_count ON ticket USING btree (comment_count);

CREATE INDEX ix_ticket_scoped_id ON ticket USING btree (scoped_id);

CREATE INDEX ticket_scoped_id ON ticket USING btree (scoped_id);

CREATE INDEX ticket_tracker_id ON ticket USING btree (tracker_id);

CREATE INDEX ticket_dupe_of_id ON ticket USING btree (dupe_of_id);

CREATE TABLE ticket_assignee (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	updated timestamp without time zone NOT NULL,
	ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE,
	assignee_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	assigner_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	CONSTRAINT idx_ticket_assignee_unique UNIQUE (ticket_id, assignee_id)
);

CREATE INDEX ticket_assignee_ticket_id ON ticket_assignee USING btree (ticket_id);

CREATE TABLE ticket_comment (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	updated timestamp without time zone NOT NULL,
	ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE,
	text character varying(16384),
	submitter_id integer NOT NULL REFERENCES participant(id),
	authenticity integer DEFAULT 0 NOT NULL,
	superceeded_by_id integer REFERENCES ticket_comment(id) ON DELETE SET NULL
);

CREATE INDEX ticket_comment_submitter_id ON ticket_comment USING btree (submitter_id);

CREATE INDEX ticket_comment_superceeded_by_id ON ticket_comment USING btree (superceeded_by_id);

CREATE INDEX ticket_comment_ticket_id ON ticket_comment USING btree (ticket_id);

CREATE TABLE ticket_label (
	ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE,
	label_id integer NOT NULL REFERENCES label(id) ON DELETE CASCADE,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	created timestamp without time zone NOT NULL,
	PRIMARY KEY (ticket_id, label_id)
);

CREATE INDEX ticket_label_ticket_id ON ticket_label USING btree (ticket_id);

CREATE TABLE ticket_subscription (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	updated timestamp without time zone NOT NULL,
	ticket_id integer REFERENCES ticket(id) ON DELETE CASCADE,
	tracker_id integer REFERENCES tracker(id) ON DELETE CASCADE,
	participant_id integer REFERENCES participant(id) ON DELETE CASCADE,
	CONSTRAINT subscription_ticket_participant_uq UNIQUE (ticket_id, participant_id),
	CONSTRAINT subscription_tracker_participant_uq UNIQUE (tracker_id, participant_id)
);

CREATE TABLE event (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	event_type integer NOT NULL,
	old_status integer,
	old_resolution integer,
	new_status integer,
	new_resolution integer,
	ticket_id integer REFERENCES ticket(id) ON DELETE CASCADE,
	comment_id integer REFERENCES ticket_comment(id) ON DELETE CASCADE,
	label_id integer REFERENCES label(id) ON DELETE CASCADE,
	from_ticket_id integer REFERENCES ticket(id) ON DELETE CASCADE,
	participant_id integer REFERENCES participant(id) ON DELETE CASCADE,
	by_participant_id integer REFERENCES participant(id) ON DELETE CASCADE
);

CREATE INDEX event_comment_id ON event USING btree (comment_id);

CREATE INDEX event_from_ticket_id ON event USING btree (from_ticket_id);

CREATE INDEX event_label_id ON event USING btree (label_id);

CREATE INDEX event_participant_id ON event USING btree (participant_id);

CREATE INDEX event_ticket_id ON event USING btree (ticket_id);

CREATE TABLE event_notification (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	event_id integer NOT NULL REFERENCES event(id) ON DELETE CASCADE,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE
);

CREATE INDEX event_notification_event_id ON event_notification USING btree (event_id);

-- GraphQL webhooks
CREATE TABLE gql_user_wh_sub (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	events webhook_event[] NOT NULL,
	url character varying NOT NULL,
	query character varying NOT NULL,
	auth_method auth_method NOT NULL,
	token_hash character varying(128),
	grants character varying,
	client_id uuid,
	expires timestamp without time zone,
	node_id character varying,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	CONSTRAINT gql_user_wh_sub_auth_method_check
		CHECK ((auth_method = ANY (ARRAY['OAUTH2'::auth_method, 'INTERNAL'::auth_method]))),
	CONSTRAINT gql_user_wh_sub_check
		CHECK (((auth_method = 'OAUTH2'::auth_method) = (token_hash IS NOT NULL))),
	CONSTRAINT gql_user_wh_sub_check1
		CHECK (((auth_method = 'OAUTH2'::auth_method) = (expires IS NOT NULL))),
	CONSTRAINT gql_user_wh_sub_check2
		CHECK (((auth_method = 'INTERNAL'::auth_method) = (node_id IS NOT NULL))),
	CONSTRAINT gql_user_wh_sub_events_check
		CHECK ((array_length(events, 1) > 0))
);

CREATE INDEX gql_user_wh_sub_token_hash_idx ON gql_user_wh_sub USING btree (token_hash);

CREATE TABLE gql_user_wh_delivery (
	id serial PRIMARY KEY,
	uuid uuid NOT NULL,
	date timestamp without time zone NOT NULL,
	event webhook_event NOT NULL,
	subscription_id integer NOT NULL REFERENCES gql_user_wh_sub(id) ON DELETE CASCADE,
	request_body character varying NOT NULL,
	response_body character varying,
	response_headers character varying,
	response_status integer
);

CREATE TABLE gql_tracker_wh_sub (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	events tracker_webhook_event[] NOT NULL,
	url character varying NOT NULL,
	query character varying NOT NULL,
	auth_method auth_method NOT NULL,
	token_hash character varying(128),
	grants character varying,
	client_id uuid,
	expires timestamp without time zone,
	node_id character varying,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
	CONSTRAINT gql_tracker_wh_sub_auth_method_check
		CHECK ((auth_method = ANY (ARRAY['OAUTH2'::auth_method, 'INTERNAL'::auth_method]))),
	CONSTRAINT gql_tracker_wh_sub_check
		CHECK (((auth_method = 'OAUTH2'::auth_method) = (token_hash IS NOT NULL))),
	CONSTRAINT gql_tracker_wh_sub_check1
		CHECK (((auth_method = 'OAUTH2'::auth_method) = (expires IS NOT NULL))),
	CONSTRAINT gql_tracker_wh_sub_check2
		CHECK (((auth_method = 'INTERNAL'::auth_method) = (node_id IS NOT NULL))),
	CONSTRAINT gql_tracker_wh_sub_events_check
		CHECK ((array_length(events, 1) > 0))
);

CREATE INDEX gql_tracker_wh_sub_token_hash_idx ON gql_tracker_wh_sub USING btree (token_hash);

CREATE TABLE gql_tracker_wh_delivery (
	id serial PRIMARY KEY,
	uuid uuid NOT NULL,
	date timestamp without time zone NOT NULL,
	event tracker_webhook_event NOT NULL,
	subscription_id integer NOT NULL REFERENCES gql_tracker_wh_sub(id) ON DELETE CASCADE,
	request_body character varying NOT NULL,
	response_body character varying,
	response_headers character varying,
	response_status integer
);

CREATE TABLE gql_ticket_wh_sub (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	events ticket_webhook_event[] NOT NULL,
	url character varying NOT NULL,
	query character varying NOT NULL,
	auth_method auth_method NOT NULL,
	token_hash character varying(128),
	grants character varying,
	client_id uuid,
	expires timestamp without time zone,
	node_id character varying,
	user_id integer NOT NULL REFERENCES "user"(id) ON DELETE CASCADE,
	tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE,
	ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE,
	scoped_id integer NOT NULL,
	CONSTRAINT gql_ticket_wh_sub_auth_method_check
		CHECK ((auth_method = ANY (ARRAY['OAUTH2'::auth_method, 'INTERNAL'::auth_method]))),
	CONSTRAINT gql_ticket_wh_sub_check
		CHECK (((auth_method = 'OAUTH2'::auth_method) = (token_hash IS NOT NULL))),
	CONSTRAINT gql_ticket_wh_sub_check1
		CHECK (((auth_method = 'OAUTH2'::auth_method) = (expires IS NOT NULL))),
	CONSTRAINT gql_ticket_wh_sub_check2
		CHECK (((auth_method = 'INTERNAL'::auth_method) = (node_id IS NOT NULL))),
	CONSTRAINT gql_ticket_wh_sub_events_check
		CHECK ((array_length(events, 1) > 0))
);

CREATE INDEX gql_ticket_wh_sub_token_hash_idx ON gql_ticket_wh_sub USING btree (token_hash);

CREATE TABLE gql_ticket_wh_delivery (
	id serial PRIMARY KEY,
	uuid uuid NOT NULL,
	date timestamp without time zone NOT NULL,
	event ticket_webhook_event NOT NULL,
	subscription_id integer NOT NULL REFERENCES gql_ticket_wh_sub(id) ON DELETE CASCADE,
	request_body character varying NOT NULL,
	response_body character varying,
	response_headers character varying,
	response_status integer
);

-- Legacy OAuth (TODO: Remove)
CREATE TABLE oauthtoken (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	updated timestamp without time zone NOT NULL,
	expires timestamp without time zone NOT NULL,
	token_hash character varying(128) NOT NULL,
	token_partial character varying(8) NOT NULL,
	scopes character varying(512) NOT NULL,
	user_id integer REFERENCES "user"(id) ON DELETE CASCADE
);

-- Legacy webhooks (TODO: Remove)
CREATE TABLE user_webhook_subscription (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	url character varying(2048) NOT NULL,
	events character varying NOT NULL,
	user_id integer REFERENCES "user"(id) ON DELETE CASCADE,
	token_id integer REFERENCES oauthtoken(id) ON DELETE CASCADE
);

CREATE TABLE user_webhook_delivery (
	id serial PRIMARY KEY,
	uuid uuid NOT NULL,
	created timestamp without time zone NOT NULL,
	event character varying(256) NOT NULL,
	url character varying(2048) NOT NULL,
	payload character varying(65536) NOT NULL,
	payload_headers character varying(16384) NOT NULL,
	response character varying(65536),
	response_status integer NOT NULL,
	response_headers character varying(16384),
	subscription_id integer NOT NULL REFERENCES user_webhook_subscription(id) ON DELETE CASCADE
);

CREATE TABLE tracker_webhook_subscription (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	url character varying(2048) NOT NULL,
	events character varying NOT NULL,
	user_id integer REFERENCES "user"(id) ON DELETE CASCADE,
	token_id integer REFERENCES oauthtoken(id) ON DELETE CASCADE,
	tracker_id integer NOT NULL REFERENCES tracker(id) ON DELETE CASCADE
);

CREATE TABLE tracker_webhook_delivery (
	id serial PRIMARY KEY,
	uuid uuid NOT NULL,
	created timestamp without time zone NOT NULL,
	event character varying(256) NOT NULL,
	url character varying(2048) NOT NULL,
	payload character varying(65536) NOT NULL,
	payload_headers character varying(16384) NOT NULL,
	response character varying(65536),
	response_status integer NOT NULL,
	response_headers character varying(16384),
	subscription_id integer NOT NULL REFERENCES tracker_webhook_subscription(id) ON DELETE CASCADE
);

CREATE TABLE ticket_webhook_subscription (
	id serial PRIMARY KEY,
	created timestamp without time zone NOT NULL,
	url character varying(2048) NOT NULL,
	events character varying NOT NULL,
	user_id integer REFERENCES "user"(id) ON DELETE CASCADE,
	token_id integer REFERENCES oauthtoken(id) ON DELETE CASCADE,
	ticket_id integer NOT NULL REFERENCES ticket(id) ON DELETE CASCADE
);

CREATE TABLE ticket_webhook_delivery (
	id serial PRIMARY KEY,
	uuid uuid NOT NULL,
	created timestamp without time zone NOT NULL,
	event character varying(256) NOT NULL,
	url character varying(2048) NOT NULL,
	payload character varying(65536) NOT NULL,
	payload_headers character varying(16384) NOT NULL,
	response character varying(65536),
	response_status integer NOT NULL,
	response_headers character varying(16384),
	subscription_id integer NOT NULL REFERENCES ticket_webhook_subscription(id) ON DELETE CASCADE
);

CREATE INDEX ticket_webhook_subscription_ticket_id ON ticket_webhook_subscription USING btree (ticket_id);
