\set database_name teamdraft
\set schema :database_name
\set owner :database_name
\set rw_name :database_name '_website'
\set ro_name :database_name '_ro'


DROP ROLE IF EXISTS ddl;

CREATE ROLE ddl WITH NOLOGIN;

DROP ROLE IF EXISTS dml;

CREATE ROLE dml WITH NOLOGIN;

DROP ROLE IF EXISTS READ_ONLY;

CREATE ROLE READ_ONLY WITH NOLOGIN;

DROP SCHEMA IF EXISTS :schema CASCADE;

DROP DATABASE IF EXISTS :database_name;

DROP ROLE IF EXISTS :owner;

CREATE ROLE :owner WITH LOGIN PASSWORD :'[REDACTED]

GRANT ddl TO :owner;

DROP ROLE IF EXISTS :rw_name;

CREATE ROLE :rw_name WITH LOGIN PASSWORD :'[REDACTED]

GRANT dml TO :rw_name;

DROP ROLE IF EXISTS :ro_name;

CREATE ROLE :ro_name WITH LOGIN PASSWORD :'[REDACTED]

GRANT READ_ONLY TO :ro_name;

ALTER ROLE :owner SET se arch_path TO :schema, pgsodium, public;

ALTER ROLE :rw_name SET search_path TO :schema, pgsodium, public;

ALTER ROLE :ro_name SET search_path TO :schema, pgsodium, public;

CREATE DATABASE :database_name OWNER :owner;

REVOKE CREATE ON SCHEMA public FROM PUBLIC;

REVOKE ALL ON DATABASE :database_name FROM PUBLIC;

GRANT CONNECT, TEMPORARY ON DATABASE :database_name TO ddl, dml, READ_ONLY;

\c :database_name
CREATE EXTENSION pgsodium;
GRANT EXECUTE ON ROUTINE pgsodium.randombytes_random TO :owner, :rw_name, :ro_name;
SET ROLE :owner;

CREATE SCHEMA :schema AUTHORIZATION :schema;

GRANT USAGE, CREATE ON SCHEMA :schema TO ddl;

GRANT USAGE ON SCHEMA :schema TO dml, READ_ONLY;

GRANT ALL ON SCHEMA pgsodium TO dml;

REVOKE ALL ON DATABASE :database_name FROM PUBLIC;

GRANT CONNECT, TEMPORARY ON DATABASE :database_name TO ddl, dml, READ_ONLY;

ALTER DEFAULT PRIVILEGES IN SCHEMA :schema GRANT
SELECT
, INSERT, UPDATE, DELETE ON TABLES TO dml;

ALTER DEFAULT PRIVILEGES IN SCHEMA :schema GRANT
SELECT
    ON TABLES TO READ_ONLY;

ALTER DEFAULT PRIVILEGES IN SCHEMA :schema GRANT USAGE,
SELECT
    ON SEQUENCES TO dml, READ_ONLY;

ALTER DEFAULT PRIVILEGES IN SCHEMA :schema GRANT UPDATE ON SEQUENCES TO dml;

ALTER DEFAULT PRIVILEGES IN SCHEMA :schema GRANT EXECUTE ON ROUTINES TO dml, READ_ONLY;


CREATE TABLE :schema.users(
    user_id integer NOT NULL GENERATED ALWAYS AS IDENTITY,
    username text,
    PASSWORD text,
    name text,
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),

    CONSTRAINT users_pk PRIMARY KEY (user_id)
);

CREATE TABLE sessions(
    session_id text PRIMARY KEY DEFAULT to_hex(pgsodium.randombytes_random()),
    user_id integer NOT NULL,
    expires timestamptz NOT NULL DEFAULT now() + interval '1 year',
    created timestamptz NOT NULL DEFAULT now(),
    modified timestamptz NOT NULL DEFAULT now(),

    CONSTRAINT sessions_users_fk FOREIGN KEY (user_id) REFERENCES users(user_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION
);

CREATE TYPE :schema.divisions AS ENUM(
    'nfcn',
    'nfce',
    'nfcs',
    'nfcw',
    'afcn',
    'afce',
    'afcs',
    'afcw'
);

CREATE TABLE :schema.teams(
    team_id smallint NOT NULL GENERATED ALWAYS AS IDENTITY,
    name text NOT NULL,
    division :schema.divisions NOT NULL,
    external_id text,
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),
    CONSTRAINT teams_pk PRIMARY KEY (team_id),
    CONSTRAINT teams_name_ck CHECK (char_length(name) <= 255)
);

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Buffalo', 'afce', '134918');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Miami', 'afce', '134919');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('New England', 'afce', '134920');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('NYJ', 'afce', '134921');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Baltimore', 'afcn', '134922');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Cincinnati', 'afcn', '134923');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Cleveland', 'afcn', '134924');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Pittsburgh', 'afcn', '134925');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Houston', 'afcs', '134926');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Indianapolis', 'afcs', '134927');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Jacksonville', 'afcs', '134928');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Tennessee', 'afcs', '134929');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Denver', 'afcw', '134930');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Kansas City', 'afcw', '134931');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Las Vegas', 'afcw', '134932');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('LAC', 'afcw', '135908');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Dallas', 'nfce', '134934');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('NYG', 'nfce', '134935');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Philadelphia', 'nfce', '134936');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Washington', 'nfce', '134937');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Chicago', 'nfcn', '134938');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Detroit', 'nfcn', '134939');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Green Bay', 'nfcn', '134940');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Minnesota', 'nfcn', '134941');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Atlanta', 'nfcs', '134942');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Carolina', 'nfcs', '134943');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('New Orleans', 'nfcs', '134944');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Tampa Bay', 'nfcs', '134945');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Seattle', 'nfcw', '134949');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('San Francisco', 'nfcw', '134948');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('Arizona', 'nfcw', '134946');

INSERT INTO :schema.teams(name, division, external_id)
    VALUES ('LAR', 'nfcw', '135907');

CREATE TABLE :schema.seasons(
    season_id smallint NOT NULL GENERATED ALWAYS AS IDENTITY,
    started_at timestamptz NOT NULL,
    ended_at timestamptz NOT NULL,
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),
    CONSTRAINT seasons_pk PRIMARY KEY (season_id)
);

CREATE TABLE :schema.leagues(
    league_id integer NOT NULL GENERATED ALWAYS AS IDENTITY,
    name text,
    user_id integer NOT NULL,
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),

    CONSTRAINT leagues_pk PRIMARY KEY (league_id),
    CONSTRAINT leagues_name_ck CHECK (char_length(name) <= 255),
    CONSTRAINT leagues_users_fk FOREIGN KEY (user_id) REFERENCES :schema.users(user_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION
);

CREATE TABLE :schema.league_seasons(
    league_season_id integer NOT NULL GENERATED ALWAYS AS IDENTITY,
    season_id smallint NOT NULL,
    league_id integer NOT NULL,
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),

    UNIQUE (season_id, league_id),
    CONSTRAINT league_seasons_pk PRIMARY KEY (league_season_id),
    CONSTRAINT league_seasons_seasons_fk FOREIGN KEY (season_id) REFERENCES :schema.seasons(season_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION,
    CONSTRAINT league_seasons_leagues_fk FOREIGN KEY (league_id) REFERENCES :schema.leagues(league_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION
);

CREATE TABLE :schema.league_users(
    league_user_id bigint NOT NULL GENERATED ALWAYS AS IDENTITY,
    league_season_id integer NOT NULL,
    user_id integer NOT NULL,
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),

    CONSTRAINT league_users_pk PRIMARY KEY (league_user_id),
    CONSTRAINT league_users_league_seasons_fk FOREIGN KEY (league_season_id) REFERENCES :schema.league_seasons(league_season_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION,
    CONSTRAINT league_users_users_fk FOREIGN KEY (user_id) REFERENCES :schema.users(user_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION
);

CREATE TABLE :schema.rosters(
    roster_id integer NOT NULL GENERATED ALWAYS AS IDENTITY,
    league_user_id bigint NOT NULL,
    team_id smallint NOT NULL,
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),
    CONSTRAINT rosters_pk PRIMARY KEY (roster_id),
    CONSTRAINT rosters_league_users_fk FOREIGN KEY (league_user_id) REFERENCES :schema.league_users(league_user_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION,
    CONSTRAINT rosters_teams_fk FOREIGN KEY (team_id) REFERENCES :schema.teams(team_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION
);

CREATE TYPE :schema.draft_status AS ENUM(
    'created',
    'running',
    'complete'
);

CREATE TABLE :schema.drafts(
    draft_id integer NOT NULL GENERATED ALWAYS AS IDENTITY,
    league_season_id integer NOT NULL,
    status :schema.draft_status NOT NULL DEFAULT 'created',
    started_at timestamptz NOT NULL,
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),
    CONSTRAINT drafts_pk PRIMARY KEY (draft_id),
    CONSTRAINT drafts_league_seasons_fk FOREIGN KEY (league_season_id) REFERENCES :schema.league_seasons(league_season_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION
);

CREATE TABLE :schema.picks(
    pick_id integer NOT NULL GENERATED ALWAYS AS IDENTITY,
    draft_id integer NOT NULL,
    league_user_id integer NOT NULL,
    team_id smallint NOT NULL,
    auto boolean NOT NULL DEFAULT FALSE,
    picked_at timestamptz NOT NULL DEFAULT now(),
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),
    UNIQUE (draft_id, team_id),
    CONSTRAINT picks_pk PRIMARY KEY (pick_id),
    CONSTRAINT picks_drafts_fk FOREIGN KEY (draft_id) REFERENCES :schema.drafts(draft_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION,
    CONSTRAINT picks_league_users_fk FOREIGN KEY (league_user_id) REFERENCES :schema.league_users(league_user_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION,
    CONSTRAINT picks_teams_fk FOREIGN KEY (team_id) REFERENCES :schema.teams(team_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION
);

CREATE TABLE :schema.rankings(
    ranking_id integer NOT NULL GENERATED ALWAYS AS IDENTITY,
    season_id integer NOT NULL,
    team_id integer NOT NULL,
    rank smallint NOT NULL,
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),
    CONSTRAINT rankings_pk PRIMARY KEY (ranking_id),
    CONSTRAINT rankings_seasons_fk FOREIGN KEY (season_id) REFERENCES :schema.seasons(season_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION
);

CREATE TYPE :schema.score_category AS ENUM(
    'win',
    'playoffs',
    'divisional',
    'conference',
    'superbowl',
    'champion'
);

CREATE TABLE :schema.scores(
    score_id integer NOT NULL GENERATED ALWAYS AS IDENTITY,
    season_id integer NOT NULL,
    team_id integer NOT NULL,
    week smallint NOT NULL,
    category :schema.score_category NOT NULL DEFAULT 'win',
    scored_at timestamptz NOT NULL DEFAULT now(),
    created_at timestamptz NOT NULL DEFAULT now(),
    modified_at timestamptz NOT NULL DEFAULT now(),
    UNIQUE (season_id, team_id, week, category),
    CONSTRAINT scores_pk PRIMARY KEY (score_id),
    CONSTRAINT scores_seasons_fk FOREIGN KEY (season_id) REFERENCES :schema.seasons(season_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION,
    CONSTRAINT scores_teams_fk FOREIGN KEY (team_id) REFERENCES :schema.teams(team_id) MATCH SIMPLE ON DELETE NO ACTION ON UPDATE NO ACTION
);

-- functions

CREATE OR REPLACE FUNCTION :schema.current_season()
    RETURNS int
    AS $$
    SELECT
        season_id
    FROM
        seasons
    WHERE
        started_at <= now()
        AND ended_at > now()
    LIMIT 1;
$$
LANGUAGE SQL;

CREATE OR REPLACE FUNCTION season_id_by_year(year text)
    RETURNS int
    AS $$
    SELECT
        season_id
    FROM
        seasons
    WHERE
        year::integer >= date_part('year', started_at)
    LIMIT 1;
$$
LANGUAGE SQL;

CREATE OR REPLACE FUNCTION category_score(category score_category)
    RETURNS smallint
    LANGUAGE plpgsql
    SECURITY INVOKER
    AS $$
BEGIN
    RETURN(
        SELECT
            CASE WHEN category = 'win' THEN
                1
            WHEN category IN('superbowl', 'conference') THEN
                10
            ELSE
                5
            END);
END;
$$;

CREATE OR REPLACE FUNCTION :schema.current_week()
    RETURNS smallint
    LANGUAGE plpgsql
    SECURITY INVOKER
    AS $$
DECLARE
    current_week smallint;
BEGIN
    SELECT
        TRUNC(DATE_PART('day', now() - started_at) / 7)::smallint + 1 AS current_week
    FROM
        seasons
    WHERE
        season_id = current_season() INTO current_week;
    RETURN (IFcurrent_week;
END;
$$;

CREATE OR REPLACE PROCEDURE new_league_season(league_id integer)
LANGUAGE plpgsql
SECURITY INVOKER
AS $$
BEGIN
    CALL new_league_season(league_id, NULL);
END;
$$;

CREATE OR REPLACE PROCEDURE new_league_season(league_id integer, draft_time timestamptz)
LANGUAGE plpgsql
SECURITY INVOKER
AS $$
DECLARE
    new_league_season_id integer;
    new_draft_id integer;
BEGIN
    INSERT INTO league_seasons(season_id, league_id)
        VALUES (current_season(), league_id)
    RETURNING
        league_season_id INTO new_league_season_id;

    IF draft_time IS NOT NULL THEN
        INSERT INTO drafts(league_season_id, started_at)
            VALUES (new_league_season_id, draft_time)
        RETURNING
            draft_id INTO new_draft_id;
    END IF;
END;
$$;

CREATE OR REPLACE FUNCTION pick(_draft_id integer, team_id integer)
    RETURNS boolean
    LANGUAGE plpgsql
    SECURITY INVOKER
    AS $$
DECLARE
    pick_count smallint;
    player_1 integer;
    player_2 integer;
BEGIN
    INSERT INTO picks (draft_id, league_user_id, team_id) VALUES (_draft_id, current_picker(_draft_id), team_id);
END;
$$;

CREATE OR REPLACE FUNCTION current_picker(_draft_id integer)
    RETURNS integer
    LANGUAGE plpgsql
    SECURITY INVOKER
    AS $$
DECLARE
    pick_count smallint;
    player_1 integer;
    player_2 integer;
BEGIN
    SELECT
        count(1)
    FROM
        picks
    WHERE
        draft_id = _draft_id INTO pick_count;
    SELECT
        league_user_id
    FROM
        league_users
    WHERE
        league_season_id =(
            SELECT
                league_season_id
            FROM
                drafts
            WHERE
                draft_id = _draft_id)
    ORDER BY
        league_user_id
    LIMIT 1 INTO player_1;
    SELECT
        league_user_id
    FROM
        league_users
    WHERE
        league_season_id =(
            SELECT
                league_season_id
            FROM
                drafts
            WHERE
                draft_id = _draft_id)
    ORDER BY
        league_user_id DESC
    LIMIT 1 INTO player_2;
    IF pick_count % 2 = 0 THEN
        RETURN player_1;
    ELSE
        RETURN player_2;
    END IF;
END;
$$;

-- create missing draft picks if any
CREATE OR REPLACE PROCEDURE auto_draft(_draft_id integer)
LANGUAGE plpgsql
SECURITY INVOKER
AS $$
DECLARE
    latest_pick_time timestamptz;
    latest_pick picks%ROWTYPE;
    pick_count integer;
    next_auto_draft_team integer;
    draft drafts%ROWTYPE;
BEGIN
    SELECT * FROM drafts WHERE draft_id = _draft_id INTO draft;
    /* IF now() >= draft.started_at THEN */
    /*     UPDATE drafts SET status = 'running', modified_at = now() WHERE draft_id = _draft_id; */
    /* END IF; */
    latest_pick_time = draft.started_at;
    
    /* SELECT */
    /*     started_at */
    /* FROM */
    /*     drafts */
    /* WHERE */
    /*     draft_id = _draft_id INTO latest_pick_time; */

    IF now() >= latest_pick_time AND draft.status = 'created' THEN
        UPDATE drafts SET status = 'running', modified_at = now() WHERE draft_id = _draft_id;
    END IF;

    LOOP
        SELECT
            count(1)
        FROM
            picks
        WHERE
            draft_id = _draft_id INTO pick_count;

        IF pick_count = 32 THEN
            UPDATE drafts SET status = 'complete', modified_at = now() WHERE draft_id = _draft_id;
            RETURN;
        END IF;

        SELECT
            *
        FROM
            picks
        WHERE
            draft_id = _draft_id
        ORDER BY
            picked_at DESC
        LIMIT 1 INTO latest_pick;
        IF latest_pick IS NOT NULL THEN
            latest_pick_time = latest_pick.picked_at;
        END IF;
        IF (date_part('epoch', now())::integer - date_part('epoch', latest_pick_time)::integer) / 30 >= 1 THEN
            SELECT
                current_rankings.team_id
            FROM
                current_rankings
            LEFT JOIN picks ON picks.team_id = current_rankings.team_id
        WHERE
            pick_id IS NULL
        ORDER BY
            rank ASC
        LIMIT 1 INTO next_auto_draft_team;
            INSERT INTO picks(draft_id, league_user_id, team_id, auto, picked_at)
                VALUES (_draft_id, current_picker(_draft_id),
(
                        SELECT
                            current_rankings.team_id
                        FROM
                            current_rankings
                        LEFT JOIN picks ON picks.team_id = current_rankings.team_id
                        AND picks.draft_id = _draft_id
                    WHERE
                        pick_id IS NULL
                    ORDER BY
                        rank ASC
                    LIMIT 1),
                TRUE,
                latest_pick_time + interval '30 seconds');
        ELSE
            EXIT;
        END IF;
    END LOOP;
END;
$$;

CREATE OR REPLACE FUNCTION current_round(_draft_id integer)
    RETURNS smallint
    LANGUAGE plpgsql
    SECURITY INVOKER
    AS $$
DECLARE
    time_now timestamptz;
    round smallint;
    latest_pick picks%ROWTYPE;
    pick_count integer;
BEGIN
    time_now = now();
    SELECT
        count(1)
    FROM
        picks
    WHERE
        draft_id = _draft_id INTO pick_count;
    SELECT
        *
    FROM
        picks
    WHERE
        draft_id = _draft_id
    ORDER BY
        created_at DESC
    LIMIT 1 INTO latest_pick;
    IF latest_pick IS NOT NULL THEN
        time_now = latest_pick.created_at;
    END IF;
    SELECT
        (date_part('epoch', time_now)::integer - date_part('epoch', started_at)::integer) / 30
    FROM
        drafts
    WHERE
        draft_id = _draft_id INTO round;
    IF round >= 32 THEN
        RETURN 32;
    ELSE
        RETURN (round);
    END IF;
END;
$$;

CREATE OR REPLACE FUNCTION record_external_score_by_year(_external_id text, year int, week smallint, category score_category, scored_at timestamptz)
    RETURNS int
    LANGUAGE plpgsql
    SECURITY INVOKER
    AS $$
DECLARE
    team_id integer;
    season_id integer;
BEGIN
    SELECT seasons.season_id FROM seasons WHERE date_part('year', started_at) = year ORDER BY started_at DESC LIMIT 1 INTO season_id;
    SELECT teams.team_id FROM teams WHERE external_id = _external_id INTO team_id;
    RETURN (SELECT record_score(team_id, week, season_id, category, scored_at));
END;
$$;

CREATE OR REPLACE FUNCTION record_external_score(_external_id text, season_id int, week smallint, category score_category, scored_at timestamptz)
    RETURNS int
    LANGUAGE plpgsql
    SECURITY INVOKER
    AS $$
DECLARE
    team_id integer;
BEGIN
    SELECT teams.team_id FROM teams WHERE external_id = _external_id INTO team_id;
    RETURN (SELECT record_score(team_id, week, season_id, category, scored_at));
END;
$$;


CREATE OR REPLACE FUNCTION record_score(team_id int, week smallint, season_id int, category score_category, scored_at timestamptz)
    RETURNS int
    LANGUAGE plpgsql
    SECURITY INVOKER
    AS $$
DECLARE
    new_score_id integer;
BEGIN
    INSERT INTO scores(season_id, team_id, week, category, scored_at)
        VALUES (season_id, team_id, week, category, scored_at)
    ON CONFLICT ON CONSTRAINT scores_season_id_team_id_week_category_key
        DO UPDATE SET modified_at = NOW(), scored_at = EXCLUDED.scored_at
    RETURNING
        score_id INTO new_score_id;
    RETURN new_score_id;
END;
$$;

CREATE OR REPLACE FUNCTION current_draft_picks(_draft_id int)
    RETURNS TABLE (team_id      smallint,
                   rank         smallint,
                   name         text,
                   division     divisions,
                   pick_user    text)
    LANGUAGE plpgsql
    SECURITY INVOKER
    AS $$
DECLARE
    draft_season_id integer;
BEGIN
    SELECT league_seasons.season_id FROM drafts JOIN league_seasons USING (league_season_id) WHERE draft_id = _draft_id INTO draft_season_id;
RETURN QUERY 
    WITH season_rankings AS (
        SELECT
            teams.team_id,
            rankings.rank,
            teams.name,
            teams.division
        FROM
            teams
            LEFT JOIN rankings ON rankings.team_id = teams.team_id
                AND rankings.season_id = draft_season_id
            ORDER BY
                rank
    )
    SELECT r.*, users.name AS pick_user 
    FROM season_rankings r
    LEFT JOIN picks ON r.team_id = picks.team_id AND picks.draft_id = _draft_id 
    LEFT JOIN league_users ON league_users.league_user_id = picks.league_user_id
    LEFT JOIN users ON users.user_id = league_users.user_id;
END;
$$;

-- views
CREATE OR REPLACE VIEW draft_info AS
 WITH latest_draft_pick AS (
    SELECT draft_id, MAX(picked_at) AS picked_at FROM picks GROUP BY draft_id
 ) 
 select draft_id, current_picker(draft_id), status,
 leagues.name as league,
 CASE WHEN status = 'complete' OR status = 'created' THEN 0
ELSE date_part('epoch', (coalesce(latest_draft_pick.picked_at, started_at) + interval '30 seconds') - now())::integer 
    END AS round_time_remaining,
 CASE
   WHEN status = 'complete' THEN 'The draft is over, good luck!'
   WHEN status = 'created' THEN 'The draft will start in ' || to_char(started_at - now(), 'MI:SS')
   WHEN status = 'running' THEN 'It''s ' ||
     (SELECT u.name FROM league_users lu JOIN users u ON u.user_id = lu.user_id
      WHERE lu.league_user_id = current_picker(draft_id)) || '''s turn!'
 END as message,
 status = 'running' as can_pick
 from drafts
 JOIN league_seasons USING(league_season_id)
 JOIN leagues USING(league_id)
 LEFT JOIN latest_draft_pick USING(draft_id);

CREATE OR REPLACE VIEW league_picks AS
SELECT
    league_id,
    teams.team_id,
    teams.name AS team,
    users.name AS player
FROM
    picks
    JOIN teams USING (team_id)
    JOIN league_users USING (league_user_id)
    JOIN users USING (user_id)
    JOIN league_seasons USING (league_season_id)
ORDER BY
    picks.created_at;

CREATE OR REPLACE VIEW season_player_scores AS
WITH season_scores AS (
    SELECT
        season_id,
        team_id,
        sum(category_score(category))::integer AS points
    FROM
        scores
    GROUP BY
        season_id,
        team_id
)
SELECT
    season_id,
    league_id,
    player,
    sum(coalesce(points, 0))::integer AS score
FROM
    league_picks
    LEFT OUTER JOIN season_scores USING (team_id)
GROUP BY
    season_id,
    league_id,
    player
ORDER BY
    score DESC;

CREATE OR REPLACE VIEW current_player_scores AS
WITH current_scores AS (
    SELECT
        team_id,
        sum(category_score(category))::integer AS points
    FROM
        scores
    WHERE
        season_id = current_season()
    GROUP BY
        team_id
)
SELECT
    league_id,
    player,
    sum(coalesce(points, 0))::integer AS score
FROM
    league_picks
    LEFT OUTER JOIN current_scores USING (team_id)
GROUP BY
    league_id,
    player
ORDER BY
    score DESC;

CREATE OR REPLACE VIEW league_scores AS
WITH season_scores AS (
    SELECT
        season_id,
        team_id,
        SUM(CASE WHEN category = 'win' THEN
                    category_score('win')
                END)::smallint AS win,
        SUM(CASE WHEN category = 'playoffs' THEN
                    category_score('playoffs')
                END)::smallint AS playoffs,
        SUM(CASE WHEN category = 'divisional' THEN
                    category_score('divisional')
                END)::smallint AS divisional,
        SUM(CASE WHEN category = 'conference' THEN
                    category_score('conference')
                END)::smallint AS conference,
        SUM(CASE WHEN category = 'superbowl' THEN
                    category_score('superbowl')
                END)::smallint AS superbowl,
        SUM(CASE WHEN category = 'champion' THEN
                    category_score('champion')
                END)::smallint AS champion,
        coalesce(SUM(category_score(category)), 0)::smallint AS total
    FROM
        scores
    GROUP BY
        season_id,
        team_id
)
SELECT
    season_id,
    league_id,
    team,
    player,
    coalesce(win, 0) AS win,
    playoffs,
    divisional,
    conference,
    superbowl,
    champion,
    coalesce(total, 0) AS total
FROM
    league_picks
    LEFT OUTER JOIN season_scores USING (team_id);


CREATE OR REPLACE VIEW :schema.current_rankings AS
SELECT
    teams.team_id,
    rank,
    name,
    division
FROM
    teams
    LEFT JOIN rankings ON rankings.team_id = teams.team_id
        AND rankings.season_id = current_season()
    ORDER BY
        rank;

CREATE OR REPLACE FUNCTION random_invite()
    RETURNS  varchar
    LANGUAGE plpgsql
AS $$
DECLARE
    base10 bigint;
    base36 varchar := '';
    intval bigint;
    char0z char[]  := regexp_split_to_array('0123456789abcdefghijklmnopqrstuvwxyz', '');
BEGIN
    SELECT floor(random() * 78_364_164_095 + 2_176_782_336) INTO base10;
    intval := abs(base10);
    WHILE intval != 0 LOOP
        base36 := char0z[(intval % 36)+1] || base36;
        intval := intval / 36;
    END LOOP;

    IF base10 = 0 THEN base36 := '0'; END IF;
    RETURN base36;
END;
$$;
