DROP FUNCTION IF EXISTS get_subscriptions(text);
CREATE OR REPLACE FUNCTION get_subscriptions(username text) RETURNS
  TABLE(name text) AS $$
DECLARE
  uid INT;
BEGIN
  SELECT INTO uid id FROM users WHERE users.name=username;
  RETURN QUERY SELECT users.name FROM user_subscriptions
    RIGHT JOIN users ON users.id = to_id
    WHERE fro_id=uid
    ORDER BY users.name
    ;
END
$$ LANGUAGE plpgsql;


DROP FUNCTION IF EXISTS subscribe_link(text,int);
CREATE OR REPLACE FUNCTION subscribe_link(username text, linkid int)
  RETURNS bool AS $$
DECLARE
  uid int;
  result bool;
  n_title text;
  n_url text;
BEGIN
  SELECT INTO result NOT exists(SELECT * FROM user_links WHERE user_id=uid AND link_id=linkid);
  IF result THEN
    SELECT title,url INTO n_title,n_url FROM links WHERE links.id=linkid;
    PERFORM put_link(username, n_url, n_title);
  END IF;
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS delete_link(text,int);
CREATE OR REPLACE FUNCTION delete_link(username text, linkid int)
  RETURNS bool AS $$
DECLARE
  uid int;
  result bool;
BEGIN
  SELECT INTO uid id FROM users WHERE name=username;
  SELECT INTO result exists(SELECT * FROM user_links WHERE user_id=uid AND link_id=linkid);
  IF result THEN
    DELETE FROM user_links WHERE user_id=uid AND link_id=linkid;
  END IF;
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXIST has_user_shared(text, text);
CREATE OR REPLACE FUNCTION has_user_shared(username text, linkurl text) RETURNS bool AS $$
DECLARE
  result bool;
BEGIN
  SELECT INTO result EXISTS(
    SELECT 1 FROM user_links ul
    LEFT JOIN users u ON user_id=u.id
    LEFT JOIN links l ON link_id=l.id
    WHERE linkurl=l.url AND username=u.name
  );
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS get_bones(text, timestamp, int);
DROP FUNCTION IF EXISTS get_bones(text, int, timestamp);
CREATE OR REPLACE FUNCTION get_bones(username text, lim int, before timestamp)
  RETURNS TABLE(linkid int, url text, title text, posted timestamp, poster text, total_votes bigint, subscriber_vote int, shared bool) AS $$
DECLARE
  subscriber_id int;
BEGIN
  SELECT INTO subscriber_id id FROM users WHERE users.name = username;
  CREATE TEMP TABLE middle
    ON COMMIT DROP
    AS
      SELECT
        DISTINCT ON (links.url)
        links.id,links.url,links.title,links.posted,users1.name,
        total_votes(links.id),user_vote(subscriber_id,links.id),
        has_user_shared(username, links.url)
        FROM user_subscriptions
        RIGHT JOIN user_links ON user_subscriptions.to_id=user_links.user_id
        INNER JOIN links ON link_id=links.id
        INNER JOIN users ON users.id=fro_id
        LEFT JOIN users as users1 ON users1.id=to_id
        WHERE fro_id = subscriber_id
              AND
              links.posted < before ;
  RETURN QUERY SELECT * FROM middle ORDER BY middle.posted DESC LIMIT lim;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS get_bones(text, int);
CREATE OR REPLACE FUNCTION get_bones(username text, lim int)
  RETURNS TABLE(linkid int, url text, title text, posted timestamp, poster text, total_votes bigint,
                subscriber_vote int, shared bool) AS $$
DECLARE
  subscriber_id int;
BEGIN
  SELECT INTO subscriber_id id FROM users WHERE users.name = username;
  CREATE TEMP TABLE middle
    ON COMMIT DROP
    AS
      SELECT
        DISTINCT ON (links.url)
        links.id,links.url,links.title,links.posted,users1.name,
        total_votes(links.id),user_vote(subscriber_id,links.id),
        has_user_shared(username, links.url)
        FROM user_subscriptions
        RIGHT JOIN user_links ON user_subscriptions.to_id=user_links.user_id
        INNER JOIN links ON link_id=links.id
        INNER JOIN users ON users.id=fro_id
        LEFT JOIN users as users1 ON users1.id=to_id
        WHERE fro_id = subscriber_id;
  RETURN QUERY SELECT * FROM middle ORDER BY middle.posted DESC LIMIT lim;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS get_bones(text);
CREATE OR REPLACE FUNCTION get_bones(username text)
  RETURNS TABLE(linkid int, url text, title text, posted timestamp, poster text, total_votes bigint,
                subscriber_vote int, shared bool) AS $$
DECLARE
  subscriber_id int;
BEGIN
  SELECT INTO subscriber_id id FROM users WHERE users.name = username;
  CREATE TEMP TABLE middle
    ON COMMIT DROP
    AS
      SELECT
        DISTINCT ON (links.url)
        links.id,links.url,links.title,links.posted,users1.name,
        total_votes(links.id),user_vote(subscriber_id,links.id),
        has_user_shared(username, links.url)
        FROM user_subscriptions
        RIGHT JOIN user_links ON user_subscriptions.to_id=user_links.user_id
        INNER JOIN links ON link_id=links.id
        INNER JOIN users ON users.id=fro_id
        LEFT JOIN users as users1 ON users1.id=to_id
        WHERE fro_id = subscriber_id;
  RETURN QUERY SELECT * FROM middle ORDER BY middle.posted DESC;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS get_bone(text);
CREATE OR REPLACE FUNCTION get_bone(username text)
  RETURNS TABLE(name text, url text, title text, posted timestamp, linkid int, votes bigint, shared bool) AS $$
BEGIN
  RETURN QUERY SELECT users.name, links.url, links.title, links.posted, links.id, total_votes(links.id)
      FROM users
        INNER JOIN user_links ON user_links.user_id = users.id
        INNER JOIN links ON user_links.link_id = links.id
        WHERE users.name=username
        ORDER BY links.posted DESC;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS unsubscribe(text,text);
CREATE OR REPLACE FUNCTION unsubscribe(user1 text, user2 text) RETURNS int
AS $$
DECLARE
  user1_id int;
  user2_id int;
  result int;
BEGIN
  SELECT INTO user1_id id FROM users WHERE users.name = user1;
  SELECT INTO user2_id id FROM users WHERE users.name = user2;

  DELETE FROM user_subscriptions WHERE fro_id = user1_id AND to_id = user2_id;
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS subscribe(text,text);
CREATE OR REPLACE FUNCTION subscribe(user1 text, user2 text) RETURNS int
AS $$
DECLARE
  user1_id int;
  user2_id int;
  result int;
BEGIN
  SELECT INTO user1_id id FROM users WHERE users.name = user1;
  SELECT INTO user2_id id FROM users WHERE users.name = user2;

  INSERT INTO user_subscriptions (fro_id, to_id) VALUES (user1_id, user2_id) RETURNING user_subscriptions.id INTO result;
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS follows(text,text);
CREATE OR REPLACE FUNCTION follows(fro text, to_ text)
  RETURNS bool
AS $$
DECLARE
  froid int;
  toid int;
  result bool;
BEGIN
  SELECT id INTO froid FROM users WHERE name = fro;
  SELECT id INTO toid FROM users WHERE name = to_;
  SELECT INTO result exists(SELECT 1 FROM user_subscriptions WHERE fro_id=froid and to_id=toid);
  RETURN result;
END
$$ LANGUAGE plpgsql;


DROP FUNCTION IF EXISTS put_link(text,text,text);
DROP FUNCTION IF EXISTS put_link(text,text,text,timestamp);
DROP FUNCTION IF EXISTS put_link(text,text,timestamp,text);
DROP TYPE IF EXISTS link_url_type;
CREATE TYPE link_url_type AS (link_id int, user_id int);

CREATE OR REPLACE FUNCTION put_link(username text, link_url text, n_posted timestamp, link_title text default '')
  RETURNS link_url_type
AS $$
DECLARE
  n_link_id int;
  n_user_id int;
  result link_url_type;
BEGIN
  INSERT INTO links (url, title, posted) VALUES
    (link_url, link_title, n_posted) RETURNING id INTO n_link_id;
  SELECT users.id FROM users WHERE users.name = username INTO n_user_id;
  INSERT INTO user_links (user_id, link_id) VALUES (n_user_id, n_link_id);

  SELECT n_link_id INTO result.link_id;
  SELECT n_user_id INTO result.user_id;
  RETURN result;
END
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION put_link(username text, link_url text, link_title text default '')
  RETURNS link_url_type
AS $$
DECLARE
  n_link_id int;
  n_user_id int;
  result link_url_type;
BEGIN
  INSERT INTO links (url, title) VALUES
    (link_url, link_title) RETURNING id INTO n_link_id;
  SELECT users.id FROM users WHERE users.name = username INTO n_user_id;
  INSERT INTO user_links (user_id, link_id) VALUES (n_user_id, n_link_id);

  SELECT n_link_id INTO result.link_id;
  SELECT n_user_id INTO result.user_id;
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS put_ak(text,text);
CREATE OR REPLACE FUNCTION put_ak(username text, supplied_ak text) RETURNS text
AS $$
DECLARE
  uid INT;
BEGIN
  SELECT INTO uid id FROM users WHERE name = username;
  INSERT INTO user_ak (user_id,ak) VALUES (uid,supplied_ak);
  RETURN supplied_ak;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS check_ak(text,text);
CREATE OR REPLACE FUNCTION check_ak(username text, supplied_ak text) RETURNS TEXT
AS $$
DECLARE
  uid INT;
  result TEXT;
BEGIN
  SELECT INTO uid id FROM users WHERE name = username;
  DELETE FROM user_ak WHERE user_id=uid AND user_ak.ak=supplied_ak RETURNING user_ak.ak INTO result;
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS check_password(text,text);
CREATE OR REPLACE FUNCTION check_password(username text, provided_pass text) RETURNS bool
AS $$
DECLARE
  uid INT;
  stored_hash TEXT;
  hash TEXT;
  result BOOL;
BEGIN
  SELECT id,password INTO uid,stored_hash FROM users WHERE users.name = username;
  SELECT stored_hash = crypt(provided_pass, stored_hash) INTO result;
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS vote_link(text);
DROP FUNCTION IF EXISTS vote_link(text,int);
DROP FUNCTION IF EXISTS vote_link(text,text,int);
DROP FUNCTION IF EXISTS vote_link(int,text,int);
DROP TYPE IF EXISTS vote_result_type;

CREATE TYPE vote_result_type AS (myvote int, totalvotes bigint);

CREATE OR REPLACE FUNCTION vote_link(linkid int, uid int, newvote int) RETURNS vote_result_type
AS $$
BEGIN
  INSERT INTO link_votes(vote, user_id, link_id, voted) VALUES (newvote, uid, linkid, now());
  RETURN (newvote,total_votes(linkid));
END
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION vote_link(linkid int, username text, newvote int) RETURNS vote_result_type
AS $$
DECLARE
  uid INT;
BEGIN
  SELECT id INTO uid FROM users WHERE users.name = username;
  INSERT INTO link_votes(vote, user_id, link_id, voted) VALUES (newvote, uid, linkid, now());
  RETURN (newvote,total_votes(linkid));
END
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION vote_link(linkurl text, username text, newvote int) RETURNS vote_result_type
AS $$
DECLARE
  linkid INT;
BEGIN
  SELECT id INTO linkid FROM links WHERE url = linkurl;
  RETURN vote_link(linkid, username, newvote);
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS total_votes(int);
DROP FUNCTION IF EXISTS total_votes(text);
CREATE OR REPLACE FUNCTION total_votes(req_linkid int) RETURNS bigint
AS $$
DECLARE
  result BIGINT;
  linkid INT;
BEGIN
  SELECT id INTO linkid FROM links WHERE links.id = req_linkid;
  IF linkid IS NOT null THEN
    WITH RECURSIVE
      rel_links AS (
        SELECT l2.id FROM links l1 INNER JOIN links l2 ON l1.url=l2.url WHERE l1.id=linkid
      ), ordered AS (
        SELECT DISTINCT ON (user_id) vote FROM link_votes INNER JOIN rel_links ON link_id=rel_links.id ORDER BY user_id,voted DESC
      )
    SELECT sum(vote) INTO result FROM ordered;
    IF result IS null THEN
      SELECT 0 INTO result;
    END IF;
  END IF;
  RETURN result;
END
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION total_votes(linkurl text) RETURNS bigint
AS $$
DECLARE
  linkid INT;
  result BIGINT;
BEGIN
  SELECT id INTO linkid FROM links WHERE links.url = linkurl;
  SELECT total_votes(linkid) INTO result;
  IF linkid IS NOT null AND result IS null THEN
    SELECT 0 INTO result;
  END IF;
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS user_vote(int,int);
CREATE OR REPLACE FUNCTION user_vote(uid int, linkid int) RETURNS int
AS $$
DECLARE
  result INT;
BEGIN
  WITH votes_cast AS (
    SELECT link_id,vote,url FROM link_votes
      INNER JOIN links l1 ON link_id=l1.id
      WHERE user_id=uid
      ORDER BY voted DESC
  ) SELECT vote INTO result FROM links l2, votes_cast
      WHERE l2.url=votes_cast.url AND l2.id=linkid
      LIMIT 1;
  IF result IS null THEN
    SELECT 0 INTO result;
  END IF;
  RETURN result;
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS user_vote(text,int);
CREATE OR REPLACE FUNCTION user_vote(username text, linkid int) RETURNS int
AS $$
DECLARE
  uid INT;
BEGIN
  SELECT id INTO uid FROM users WHERE users.name = username;
  RETURN user_vote(uid, linkid);
END
$$ LANGUAGE plpgsql;

DROP FUNCTION IF EXISTS total_user_votes(text);
DROP FUNCTION IF EXISTS total_user_votes(int);
CREATE OR REPLACE FUNCTION total_user_votes(username text) RETURNS bigint
AS $$
DECLARE
  uid int;
  result bigint;
BEGIN
  SELECT total_user_votes(id) INTO result FROM users WHERE users.name=username;
  RETURN result;
END
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION total_user_votes(uid int) RETURNS bigint
AS $$
DECLARE
  result bigint;
BEGIN
  SELECT sum(total_votes(link_id)) INTO result FROM user_links WHERE user_id = uid;
  IF result IS NULL AND exists(SELECT 1 FROM users WHERE id=uid LIMIT 1) THEN
    SELECT 0 INTO result;
  END IF;
  RETURN result;
END
$$ LANGUAGE plpgsql;

