CREATE SCHEMA gpt;

CREATE TYPE gpt.login_method AS ENUM (
  'email',
  'google',
  'github'
);

CREATE TYPE gpt.post_status AS ENUM (
  'draft',
  'published',
  'archived'
);

CREATE TYPE gpt.address AS (
  street varchar ( 255),
  city VARCHAR(100),
  state CHAR(2),
  postal_code CHAR(5));

CREATE TABLE gpt.users (
  id serial PRIMARY KEY,
  username varchar(50) UNIQUE NOT NULL,
  email varchar(255) UNIQUE NOT NULL,
  first_name varchar(50),
  last_name varchar(50),
  created_at timestamptz NOT NULL DEFAULT NOW(),
  updated_at timestamptz NOT NULL DEFAULT NOW()
);

CREATE TABLE gpt.logins (
  id serial PRIMARY KEY,
  user_id integer NOT NULL REFERENCES gpt.users (id) ON DELETE CASCADE,
  method gpt.login_method NOT NULL,
  identifier varchar(255) NOT NULL,
  created_at timestamptz NOT NULL DEFAULT NOW(),
  updated_at timestamptz NOT NULL DEFAULT NOW(),
  UNIQUE (method, identifier)
);

CREATE TABLE gpt.posts (
  id serial PRIMARY KEY,
  user_id integer NOT NULL REFERENCES gpt.users (id) ON DELETE CASCADE,
  title varchar(255) NOT NULL,
  content text NOT NULL,
  status gpt.post_status NOT NULL DEFAULT 'draft',
  published_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT NOW(),
  updated_at timestamptz NOT NULL DEFAULT NOW()
);

CREATE TABLE gpt.comments (
  id serial PRIMARY KEY,
  post_id integer NOT NULL REFERENCES gpt.posts (id) ON DELETE CASCADE,
  user_id integer NOT NULL REFERENCES gpt.users (id) ON DELETE CASCADE,
  content text NOT NULL,
  created_at timestamptz NOT NULL DEFAULT NOW(),
  updated_at timestamptz NOT NULL DEFAULT NOW()
);

-- create indexes
CREATE INDEX user_posts ON gpt.posts (user_id, created_at DESC);

-- create views
CREATE VIEW gpt.posts_with_comments AS
SELECT
  p.id,
  p.user_id,
  p.title,
  p.content,
  p.status,
  p.published_at,
  p.created_at,
  p.updated_at,
  json_agg(json_build_object('id', c.id, 'user_id', c.user_id, 'content', c.content, 'created_at', c.created_at, 'updated_at', c.updated_at)) AS comments
FROM
  gpt.posts p
  LEFT JOIN gpt.comments c ON c.post_id = p.id
GROUP BY
  p.id;

CREATE MATERIALIZED VIEW gpt.users_with_posts AS
SELECT
  u.id,
  u.username,
  u.email,
  u.first_name,
  u.last_name,
  u.created_at,
  u.updated_at,
  json_agg(json_build_object('id', p.id, 'title', p.title, 'content', p.content, 'status', p.status, 'published_at', p.published_at, 'created_at', p.created_at, 'updated_at', p.updated_at)) AS posts
FROM
  gpt.users u
  LEFT JOIN gpt.posts p ON p.user_id = u.id
GROUP BY
  u.id;

-- create function
CREATE OR REPLACE FUNCTION gpt.refresh_users_with_posts ()
  RETURNS TRIGGER
  AS $$
BEGIN
  REFRESH MATERIALIZED VIEW gpt.users_with_posts;
  RETURN NULL;
END;
$$
LANGUAGE plpgsql;

-- create triggers
CREATE TRIGGER refresh_users_with_posts
  AFTER INSERT OR UPDATE OR DELETE ON gpt.posts
  FOR EACH STATEMENT
  EXECUTE PROCEDURE gpt.refresh_users_with_posts ();
