COMMENT ON SCHEMA public IS '{"version": "V002"}';

CREATE TYPE DRIVE_ITEM_TYPE AS ENUM ('directory', 'file');
CREATE TYPE GAME_CATEGORY AS ENUM ('big', 'small', 'puzzle', 'challenge', 'fun', 'lab', 'other');

ALTER TABLE inventories
  DROP CONSTRAINT inventory_hfest_item___fk;
ALTER TABLE hfest_items
  ALTER COLUMN hfest_item_id TYPE VARCHAR(4);
ALTER TABLE inventories
  ALTER COLUMN hfest_item_id TYPE VARCHAR(4);
ALTER TABLE inventories
  ADD CONSTRAINT inventory_hfest_item___fk FOREIGN KEY (hfest_item_id) REFERENCES hfest_items (hfest_item_id) ON DELETE CASCADE ON UPDATE CASCADE;

ALTER TABLE users
  ADD COLUMN is_tester BOOLEAN,
  ADD COLUMN email VARCHAR(200),
  ADD COLUMN username VARCHAR(20),
  ADD COLUMN password_hash BYTEA;
-- noinspection SqlWithoutWhere
UPDATE users
SET is_tester = users.is_administrator;
ALTER TABLE users
  ALTER COLUMN is_tester SET NOT NULL,
  ADD CHECK (NOT is_administrator OR is_tester);

ALTER TABLE hfest_identities
  DROP CONSTRAINT hfest_identity_identity___fk;
ALTER TABLE identities
  ADD CONSTRAINT identitity_hfest_identity___fk FOREIGN KEY (identity_id) REFERENCES hfest_identities (identity_id) ON DELETE CASCADE ON UPDATE CASCADE,
  ALTER COLUMN user_id DROP NOT NULL;

ALTER TABLE sessions
  ADD COLUMN created_at TIMESTAMP(0) WITH TIME ZONE,
  ADD COLUMN updated_at TIMESTAMP(0) WITH TIME ZONE;
-- noinspection SqlWithoutWhere
UPDATE sessions
SET created_at = sessions.expiration_date,
    updated_at = sessions.expiration_date;
ALTER TABLE sessions
  ALTER COLUMN created_at SET NOT NULL,
  ALTER COLUMN updated_at SET NOT NULL,
  DROP COLUMN expiration_date,
  ADD CHECK (updated_at >= created_at);

DROP TABLE hfest_progressions;


CREATE TABLE public.blobs
(
  blob_id    UUID PRIMARY KEY         NOT NULL,
  media_type VARCHAR(100)             NOT NULL,
  byte_size  INT4                     NOT NULL,
  buffer_id  VARCHAR(64)              NOT NULL,
  created_at TIMESTAMP WITH TIME ZONE NOT NULL,
  updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
  CHECK (updated_at >= created_at)
);

-- A table for the data common to both directories and files
CREATE TABLE public.drive_items
(
  drive_item_id UUID PRIMARY KEY         NOT NULL,
  type          DRIVE_ITEM_TYPE          NOT NULL,
  created_at    TIMESTAMP WITH TIME ZONE NOT NULL,
  updated_at    TIMESTAMP WITH TIME ZONE NOT NULL,
  display_name  VARCHAR(100)             NOT NULL,
  CHECK (updated_at >= created_at),
  UNIQUE (drive_item_id, type)
);

-- The closure table containing all the ancestor/descendant links for the directories and files
CREATE TABLE public.drive_item_closure
(
  ancestor_id   UUID NOT NULL,
  descendant_id UUID NOT NULL,
  -- Distance between the ancestor and descendant
  distance      INT4 NOT NULL,
  CONSTRAINT drive_item_closure_ancestor___fk FOREIGN KEY (ancestor_id) REFERENCES drive_items (drive_item_id) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT drive_item_closure_descendant___fk FOREIGN KEY (descendant_id) REFERENCES drive_items (drive_item_id) ON DELETE CASCADE ON UPDATE CASCADE,
  PRIMARY KEY (ancestor_id, descendant_id)
);

CREATE TABLE public.directories
(
  drive_item_id UUID PRIMARY KEY NOT NULL,
  type          DRIVE_ITEM_TYPE  NOT NULL,
  CONSTRAINT directory_drive_item___fk FOREIGN KEY (drive_item_id, type) REFERENCES drive_items (drive_item_id, type) ON DELETE CASCADE ON UPDATE CASCADE,
  CHECK (type = 'directory')
);

CREATE TABLE public.files
(
  drive_item_id UUID PRIMARY KEY NOT NULL,
  type          DRIVE_ITEM_TYPE  NOT NULL,
  blob_id       UUID             NOT NULL,
  CONSTRAINT file_drive_item___fk FOREIGN KEY (drive_item_id, type) REFERENCES drive_items (drive_item_id, type) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT file_blob___fk FOREIGN KEY (blob_id) REFERENCES blobs (blob_id) ON DELETE CASCADE ON UPDATE CASCADE,
  CHECK (type = 'file')
);

CREATE TABLE public.drives
(
  drive_id   UUID PRIMARY KEY         NOT NULL,
  owner_id   UUID                     NOT NULL,
  created_at TIMESTAMP WITH TIME ZONE NOT NULL,
  updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
  root_id    UUID                     NOT NULL,
  CONSTRAINT drive_user___fk FOREIGN KEY (owner_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT drive_directory__fk FOREIGN KEY (root_id) REFERENCES directories (drive_item_id) ON DELETE CASCADE ON UPDATE CASCADE,
  CHECK (updated_at >= created_at)
);

CREATE TABLE public.upload_sessions
(
  upload_session_id UUID PRIMARY KEY         NOT NULL,
  media_type        VARCHAR(100)             NOT NULL,
  byte_size         INT4                     NOT NULL,
  written_bytes     INT4                     NOT NULL,
  created_at        TIMESTAMP WITH TIME ZONE NOT NULL,
  updated_at        TIMESTAMP WITH TIME ZONE NOT NULL,
  buffer_id         VARCHAR(64)              NOT NULL,
  blob_id           UUID,
  CONSTRAINT upload_session_blob__fk FOREIGN KEY (blob_id) REFERENCES blobs (blob_id) ON DELETE CASCADE ON UPDATE CASCADE,
  CHECK (written_bytes <= byte_size),
  CHECK (updated_at >= created_at)
);

CREATE TABLE public.games
(
  game_id            UUID PRIMARY KEY         NOT NULL,
  display_name       VARCHAR(100)             NOT NULL,
  created_at         TIMESTAMP WITH TIME ZONE NOT NULL,
  updated_at         TIMESTAMP WITH TIME ZONE NOT NULL,
  public_access_from TIMESTAMP WITH TIME ZONE,
  author_id          UUID                     NOT NULL,
  icon_file_id       UUID,
  category           GAME_CATEGORY            NOT NULL,
  description        TEXT                     NOT NULL,
  families           TEXT                     NOT NULL,
  loader_version     VARCHAR(20)              NOT NULL,
  is_private         BOOLEAN                  NOT NULL,
  CONSTRAINT game_author__fk FOREIGN KEY (author_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
  CHECK (updated_at >= created_at)
);

CREATE TABLE public.game_modes
(
  game_id    UUID         NOT NULL,
  mode_tag   VARCHAR(100) NOT NULL,
  is_enabled BOOLEAN      NOT NULL,
  _rank      INT          NOT NULL,
  PRIMARY KEY (game_id, mode_tag),
  CONSTRAINT game_mode_game__fk FOREIGN KEY (game_id) REFERENCES games (game_id) ON DELETE CASCADE ON UPDATE CASCADE,
  UNIQUE (game_id, mode_tag, _rank)
);

CREATE TABLE public.game_options
(
  game_id    UUID         NOT NULL,
  mode_tag   VARCHAR(100) NOT NULL,
  option_tag VARCHAR(100) NOT NULL,
  is_enabled BOOLEAN      NOT NULL,
  _rank      INT          NOT NULL,
  PRIMARY KEY (game_id, mode_tag, option_tag),
  CONSTRAINT game_option_game_mode__fk FOREIGN KEY (game_id, mode_tag) REFERENCES game_modes (game_id, mode_tag) ON DELETE CASCADE ON UPDATE CASCADE,
  UNIQUE (game_id, mode_tag, option_tag, _rank)
);

CREATE TABLE public.game_resources
(
  game_id      UUID NOT NULL,
  file_id      UUID NOT NULL,
  resource_tag VARCHAR(100),
  _rank        INT  NOT NULL,
  PRIMARY KEY (game_id, file_id, _rank),
  CONSTRAINT game_resource_game__fk FOREIGN KEY (game_id) REFERENCES games (game_id) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT game_resource_file__fk FOREIGN KEY (file_id) REFERENCES files (drive_item_id) ON DELETE CASCADE ON UPDATE CASCADE
);

CREATE TABLE public.runs
(
  run_id       UUID                     NOT NULL,
  game_id      UUID                     NOT NULL,
  user_id      UUID                     NOT NULL,
  created_at   TIMESTAMP WITH TIME ZONE NOT NULL,
  started_at   TIMESTAMP WITH TIME ZONE,
  game_mode    VARCHAR(100)             NOT NULL,
  game_options JSON                     NOT NULL,
  details      BOOLEAN                  NOT NULL,
  shake        BOOLEAN                  NOT NULL,
  sound        BOOLEAN                  NOT NULL,
  music        BOOLEAN                  NOT NULL,
  PRIMARY KEY (run_id),
  CONSTRAINT run_game__fk FOREIGN KEY (game_id) REFERENCES games (game_id) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT run_file__fk FOREIGN KEY (user_id) REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE
);
