#!/bin/bash
# Runs once on first container start (docker-entrypoint-initdb.d).
# Two cases:
#   1. Fresh volume (e.g. prod on ambrosio): the supabase/postgres image's
#      initdb only creates the `postgres` role. Create all Supabase internal
#      roles here, then set their passwords.
#   2. Pre-existing volume carried over from an older build that already has
#      the roles (e.g. long-lived dev): only ALTER their passwords.
#
# Idempotent: safe on both paths.

set -e

PASS="${POSTGRES_PASSWORD:-postgres}"

# Connect as `postgres` (always exists post-initdb) on the Unix socket (trusted).
psql -v ON_ERROR_STOP=1 --username postgres --dbname postgres <<-EOSQL
    -- ── Role bootstrap (idempotent) ───────────────────────────────────────
    -- Template for most service roles: NOLOGIN OR LOGIN depending on use.

    DO \$\$ BEGIN
        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'supabase_admin') THEN
            CREATE ROLE supabase_admin SUPERUSER CREATEDB CREATEROLE REPLICATION BYPASSRLS LOGIN PASSWORD '${PASS}';
        ELSE
            ALTER ROLE supabase_admin WITH PASSWORD '${PASS}';
        END IF;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'anon') THEN
            CREATE ROLE anon NOLOGIN NOINHERIT;
        END IF;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticated') THEN
            CREATE ROLE authenticated NOLOGIN NOINHERIT;
        END IF;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'service_role') THEN
            CREATE ROLE service_role NOLOGIN NOINHERIT BYPASSRLS;
        END IF;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'authenticator') THEN
            CREATE ROLE authenticator LOGIN NOINHERIT PASSWORD '${PASS}';
        ELSE
            ALTER ROLE authenticator WITH PASSWORD '${PASS}';
        END IF;
        -- authenticator must be able to SET ROLE to anon/authenticated/service_role.
        GRANT anon, authenticated, service_role TO authenticator;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'supabase_auth_admin') THEN
            CREATE ROLE supabase_auth_admin LOGIN NOINHERIT CREATEROLE PASSWORD '${PASS}';
        ELSE
            ALTER ROLE supabase_auth_admin WITH PASSWORD '${PASS}';
        END IF;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'supabase_storage_admin') THEN
            CREATE ROLE supabase_storage_admin LOGIN NOINHERIT CREATEROLE PASSWORD '${PASS}';
        ELSE
            ALTER ROLE supabase_storage_admin WITH PASSWORD '${PASS}';
        END IF;
        GRANT authenticator TO supabase_storage_admin;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'supabase_replication_admin') THEN
            CREATE ROLE supabase_replication_admin LOGIN REPLICATION PASSWORD '${PASS}';
        ELSE
            ALTER ROLE supabase_replication_admin WITH PASSWORD '${PASS}';
        END IF;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'supabase_realtime_admin') THEN
            CREATE ROLE supabase_realtime_admin NOLOGIN NOINHERIT;
        END IF;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'pgbouncer') THEN
            CREATE ROLE pgbouncer LOGIN PASSWORD '${PASS}';
        ELSE
            ALTER ROLE pgbouncer WITH PASSWORD '${PASS}';
        END IF;

        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'dashboard_user') THEN
            CREATE ROLE dashboard_user CREATEROLE CREATEDB REPLICATION;
        END IF;
    END \$\$;

    -- Grant postgres membership in service roles so seed inserts work.
    GRANT anon, authenticated, service_role, supabase_auth_admin, supabase_storage_admin, supabase_realtime_admin TO postgres;

    -- Per-role search_path + timeouts (match dev to avoid migration drift).
    -- GoTrue's migrations issue unqualified CREATE TYPE / CREATE TABLE expecting
    -- these to land in the `auth` schema — with no default here they end up in
    -- `public` and later migrations that reference `auth.factor_type` blow up.
    ALTER ROLE supabase_auth_admin SET search_path TO auth;
    ALTER ROLE supabase_auth_admin SET idle_in_transaction_session_timeout TO 60000;
    ALTER ROLE supabase_storage_admin SET search_path TO storage;
    ALTER ROLE anon SET statement_timeout TO '3s';
    ALTER ROLE authenticated SET statement_timeout TO '8s';
    ALTER ROLE authenticator SET statement_timeout TO '8s';
    ALTER ROLE authenticator SET lock_timeout TO '8s';

    -- ── Extensions the migrations assume are enabled ─────────────────────
    -- Migration 005 invokes cron.schedule(...); dev has pg_cron auto-enabled
    -- via the image's supa-init scripts, prod does not.
    CREATE EXTENSION IF NOT EXISTS pg_cron;
    CREATE EXTENSION IF NOT EXISTS pgcrypto;
    CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

    -- ── Schemas the Supabase services expect ─────────────────────────────
    CREATE SCHEMA IF NOT EXISTS auth AUTHORIZATION supabase_auth_admin;
    CREATE SCHEMA IF NOT EXISTS storage AUTHORIZATION supabase_storage_admin;
    CREATE SCHEMA IF NOT EXISTS graphql_public;

    CREATE SCHEMA IF NOT EXISTS _realtime;
    GRANT ALL ON SCHEMA _realtime TO supabase_replication_admin;
    ALTER ROLE supabase_replication_admin SET search_path TO _realtime;

    -- Pre-create the `realtime` schema so Realtime's per-tenant migrations
    -- don't fail on "schema realtime does not exist".
    CREATE SCHEMA IF NOT EXISTS realtime AUTHORIZATION supabase_admin;

    -- Empty publication that migration 007 will ALTER with specific tables.
    -- (supabase/postgres image ships this pre-created in newer versions;
    -- on v15.1.1.78 it does not.)
    CREATE PUBLICATION supabase_realtime;

    -- PostgREST needs USAGE on public for anon + authenticated + service_role.
    GRANT USAGE ON SCHEMA public TO anon, authenticated, service_role;

    -- Default privileges for future tables in public (RLS policies still apply).
    ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO anon, authenticated, service_role;
    ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO anon, authenticated, service_role;
    ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon, authenticated, service_role;

    -- GoTrue runs its own migrations against `public.schema_migrations`. PG 15
    -- revokes CREATE from public by default, so grant it back to the service roles.
    GRANT CREATE, USAGE ON SCHEMA public TO supabase_auth_admin, supabase_storage_admin;

    -- Storage migrations call statements that require database-level grants
    -- (e.g. CREATE EXTENSION, ALTER DEFAULT PRIVILEGES).
    GRANT ALL ON DATABASE postgres TO supabase_auth_admin, supabase_storage_admin;

    -- ── Keycloak database + role (separate DB, idempotent) ───────────────
    -- CREATE DATABASE cannot run in a transaction, so run it via \gexec.
    SELECT 'CREATE DATABASE keycloak'
    WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'keycloak') \gexec

    DO \$\$ BEGIN
        IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'keycloak') THEN
            CREATE ROLE keycloak LOGIN PASSWORD '${PASS}';
        ELSE
            ALTER ROLE keycloak WITH PASSWORD '${PASS}';
        END IF;
    END \$\$;

    GRANT ALL PRIVILEGES ON DATABASE keycloak TO keycloak;
EOSQL

# Keycloak also needs CREATE on the keycloak database's public schema.
psql -v ON_ERROR_STOP=1 --username postgres --dbname keycloak <<-EOSQL
    GRANT ALL ON SCHEMA public TO keycloak;
EOSQL

echo "==> db-init: roles bootstrapped, passwords set, schemas created"
