-- Tier 2 Block C — multi-host / collaborators.
--
-- An event can now have multiple users with one of three roles:
--   owner   — full access incl. delete + manage collaborators
--   editor  — guests/tokens/branding/messages/analytics
--   viewer  — read-only
--
-- The existing events.host_id stays as a denormalised "primary owner"
-- pointer (cheap join key, useful for the existing GET /events query). The
-- source of truth for authz is event_collaborators — every handler resolves
-- the caller's role through it.
--
-- Schema #0005 in TIER2_PLAN.md; landing at 0008 because earlier slots are
-- taken by Tier 1 work.

DO $$ BEGIN
    CREATE TYPE collaborator_role AS ENUM ('owner', 'editor', 'viewer');
EXCEPTION WHEN duplicate_object THEN NULL; END $$;

CREATE TABLE IF NOT EXISTS event_collaborators (
    event_id     UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE,
    user_id      UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    role         collaborator_role NOT NULL,
    invited_by   UUID REFERENCES users(id) ON DELETE SET NULL,
    invited_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    accepted_at  TIMESTAMPTZ,
    PRIMARY KEY (event_id, user_id)
);

-- Fast lookup of "what events does this user have any role on" — used by
-- the dashboard list and the role-resolution middleware.
CREATE INDEX IF NOT EXISTS idx_collaborators_user
    ON event_collaborators(user_id);

-- Pending invitations. The invitee may not have a GuestGuard account yet;
-- the accept flow creates one (or links to an existing one by email) and
-- promotes the row into event_collaborators.
--
-- token_hash is sha256(raw); the raw token only ever lives in the email
-- link, never in the DB. consumed_at is set on successful accept so re-
-- using the link returns 410 Gone instead of silently re-adding the user.
CREATE TABLE IF NOT EXISTS collaborator_invites (
    token_hash   TEXT PRIMARY KEY,
    event_id     UUID NOT NULL REFERENCES events(id) ON DELETE CASCADE,
    email        TEXT NOT NULL,
    role         collaborator_role NOT NULL,
    invited_by   UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    expires_at   TIMESTAMPTZ NOT NULL,
    consumed_at  TIMESTAMPTZ,
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Find pending invites for an event quickly (the "Team" tab lists them).
-- Partial index keeps the working set small even on a busy event.
CREATE INDEX IF NOT EXISTS idx_collab_invites_event
    ON collaborator_invites(event_id)
    WHERE consumed_at IS NULL;

-- Backfill: every existing event's host becomes its owner. Idempotent so
-- re-running this migration after a partial failure (or against a freshly
-- restored backup that already has rows) does the right thing.
INSERT INTO event_collaborators (event_id, user_id, role, accepted_at, invited_at)
SELECT id, host_id, 'owner', created_at, created_at
FROM events
ON CONFLICT (event_id, user_id) DO NOTHING;
