-- t-paliad-149 PR 2: per-user named card layouts.
--
-- Design: docs/design-projects-page-2026-05-07.md §5b.3 (godel,
-- m-locked 2026-05-07: full drag-rearrange + named layouts).
--
-- Stores per-user named card-layout definitions for the /projects Cards
-- view. A layout is a `(facts[], density, gridColumns, showAllLevels)`
-- bundle plus a name and the is_default flag.
--
-- The very first time a user opens Cards view, the application layer
-- auto-seeds a "Standard" layout (the rich content set per design §5b.4)
-- and flips its is_default=true. From there the user can rename, create
-- new layouts, drag facts around, switch defaults, and delete (except
-- the active default — UI gates).
--
-- RLS scopes every operation to the calling user; layouts are personal
-- working state (no firm-wide / cross-user visibility v1). Partial unique
-- index keeps "at most one default per user" honest at the DB level even
-- if the application layer's tx-flip-default ever races.

-- ============================================================================
-- 1. paliad.user_card_layouts
-- ============================================================================

CREATE TABLE paliad.user_card_layouts (
    id          uuid        PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id     uuid        NOT NULL REFERENCES paliad.users(id) ON DELETE CASCADE,

    -- Display name. Free-form; user picks the language they think in.
    -- Renders verbatim in the layout dropdown; no translation.
    name        text        NOT NULL,

    -- Exactly one default per user, enforced via partial unique index below.
    -- Application layer flips this in a transaction (clear old, set new).
    is_default  boolean     NOT NULL DEFAULT false,

    -- Layout JSON — see internal/services/layout_spec.go LayoutSpec.
    -- Validated on write; jsonb here for forward-compat without migrations
    -- as new fact keys land.
    layout_json jsonb       NOT NULL,

    created_at  timestamptz NOT NULL DEFAULT now(),
    updated_at  timestamptz NOT NULL DEFAULT now(),

    -- Names are unique per user so the layout dropdown can use names as
    -- stable labels and the application layer can return ErrUserCardLayoutNameTaken.
    UNIQUE (user_id, name)
);

-- ============================================================================
-- 2. Indexes
-- ============================================================================

-- Hot path: list a user's layouts in name order.
CREATE INDEX user_card_layouts_user_idx
    ON paliad.user_card_layouts (user_id, name);

-- Partial unique index: at most one default layout per user. Keeps the
-- invariant honest even if two concurrent PATCH .../set-default calls land
-- (the second one's UPDATE will conflict, the application layer retries).
CREATE UNIQUE INDEX user_card_layouts_default_idx
    ON paliad.user_card_layouts (user_id)
    WHERE is_default = true;

-- ============================================================================
-- 3. RLS
-- ============================================================================

ALTER TABLE paliad.user_card_layouts ENABLE ROW LEVEL SECURITY;

-- Owner-only access. No global_admin override (mirrors paliad.user_views;
-- card layouts are personal working state, not auditable infrastructure).
CREATE POLICY user_card_layouts_owner_all
    ON paliad.user_card_layouts FOR ALL
    USING (user_id = auth.uid())
    WITH CHECK (user_id = auth.uid());
