-- Initial schema for mitbringsl.
-- Design notes:
--  * op_log is the append-only SOURCE OF TRUTH for sync.
--  * users/lists/items are projections of op_log (kept up to date as ops arrive).
--  * Tombstones (deleted_at) prevent resurrection of removed items by late offline ops.
--  * (client_id, client_seq) unique key on op_log makes pushes idempotent.

CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE EXTENSION IF NOT EXISTS pg_trgm;

-- ---------------------------------------------------------------------------
-- users
-- ---------------------------------------------------------------------------
CREATE TABLE users (
    id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email         TEXT NOT NULL UNIQUE,
    password_hash TEXT,                        -- NULL for OIDC-only accounts
    oidc_subject  TEXT,                        -- IdP subject id
    oidc_issuer   TEXT,                        -- which IdP the subject belongs to
    display_name  TEXT,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (oidc_issuer, oidc_subject)
);

-- ---------------------------------------------------------------------------
-- sessions (opaque tokens issued by this backend after login)
-- ---------------------------------------------------------------------------
CREATE TABLE sessions (
    id           UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id      UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    token_hash   TEXT NOT NULL UNIQUE,         -- store SHA-256 hash, never the raw token
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    expires_at   TIMESTAMPTZ NOT NULL,
    revoked_at   TIMESTAMPTZ,
    last_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    user_agent   TEXT,
    ip           TEXT
);
CREATE INDEX sessions_user_id_idx ON sessions(user_id);

-- ---------------------------------------------------------------------------
-- lists (projection)
-- ---------------------------------------------------------------------------
CREATE TABLE lists (
    id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name       TEXT NOT NULL,
    owner_id   UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    deleted_at TIMESTAMPTZ,                    -- tombstone
    hlc_ts     BIGINT NOT NULL DEFAULT 0,
    version    BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX lists_owner_idx ON lists(owner_id) WHERE deleted_at IS NULL;

-- list_members (prepared for shared lists; MVP uses owner only)
CREATE TABLE list_members (
    list_id UUID NOT NULL REFERENCES lists(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    role    TEXT NOT NULL DEFAULT 'member',    -- 'owner' | 'member'
    PRIMARY KEY (list_id, user_id)
);

-- ---------------------------------------------------------------------------
-- items (projection)
-- ---------------------------------------------------------------------------
CREATE TABLE items (
    id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    list_id    UUID NOT NULL REFERENCES lists(id) ON DELETE CASCADE,
    name       TEXT NOT NULL,
    quantity   TEXT,
    checked    BOOLEAN NOT NULL DEFAULT FALSE,
    sort_order INTEGER,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    deleted_at TIMESTAMPTZ,                    -- tombstone
    hlc_ts     BIGINT NOT NULL DEFAULT 0,
    client_id  UUID,
    checked_at TIMESTAMPTZ
);
CREATE INDEX items_list_idx ON items(list_id) WHERE deleted_at IS NULL;
CREATE INDEX items_checked_idx ON items(list_id, checked) WHERE deleted_at IS NULL;

-- ---------------------------------------------------------------------------
-- op_log (SOURCE OF TRUTH)
-- ---------------------------------------------------------------------------
CREATE TABLE op_log (
    seq        BIGSERIAL PRIMARY KEY,          -- monotonic server cursor
    list_id    UUID NOT NULL,                  -- list the op belongs to (FK added after lists exist; keep loose for tombstone cleanup)
    user_id    UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    client_id  UUID NOT NULL,
    op_type    TEXT NOT NULL,                  -- list_create|list_rename|list_delete
                                              -- item_add|item_update|item_remove
    target_id  UUID NOT NULL,                  -- affected list/item id
    payload    JSONB NOT NULL DEFAULT '{}'::jsonb,
    client_seq BIGINT NOT NULL,                -- idempotency key part
    hlc_ts     BIGINT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE (client_id, client_seq)
);
CREATE INDEX op_log_list_seq_idx ON op_log(list_id, seq);
CREATE INDEX op_log_target_idx   ON op_log(target_id);

-- ---------------------------------------------------------------------------
-- item_names (autocomplete aggregation, all users)
-- ---------------------------------------------------------------------------
CREATE TABLE item_names (
    name        TEXT PRIMARY KEY,              -- lowercased
    usage_count INTEGER NOT NULL DEFAULT 0,
    last_used_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX item_names_trgm_idx ON item_names USING gin (name gin_trgm_ops);
CREATE INDEX item_names_count_idx ON item_names(usage_count DESC);
