-- ==========================================
-- Chat Switchboard — 016 Packages
-- ==========================================
-- Unified package registry. Replaces surface_registry (v0.25.0)
-- and extensions (v0.11.0) tables. A package is a surface, an
-- extension, or both.
--
-- v0.28.7: packages table + package_user_settings in 012.
-- v0.29.0: status column + extension_permissions table.
-- v0.29.2: ext_data_tables catalog + platform read views.
-- ==========================================

CREATE TABLE IF NOT EXISTS packages (
    id           TEXT PRIMARY KEY,
    title        TEXT NOT NULL,
    type         TEXT NOT NULL DEFAULT 'surface'
                 CHECK (type IN ('surface', 'extension', 'full', 'workflow')),
    version      TEXT NOT NULL DEFAULT '0.0.0',
    description  TEXT NOT NULL DEFAULT '',
    author       TEXT NOT NULL DEFAULT '',
    tier         TEXT NOT NULL DEFAULT 'browser'
                 CHECK (tier IN ('browser', 'starlark', 'sidecar')),
    is_system    BOOLEAN NOT NULL DEFAULT false,
    scope        TEXT NOT NULL DEFAULT 'global'
                 CHECK (scope IN ('global', 'team', 'personal')),
    team_id      UUID REFERENCES teams(id) ON DELETE CASCADE,
    installed_by UUID REFERENCES users(id) ON DELETE SET NULL,
    manifest     JSONB NOT NULL DEFAULT '{}',
    enabled      BOOLEAN NOT NULL DEFAULT true,
    status       TEXT NOT NULL DEFAULT 'active'
                 CHECK (status IN ('active', 'pending_review', 'suspended')),
    schema_version INTEGER NOT NULL DEFAULT 0,
    package_settings JSONB NOT NULL DEFAULT '{}',
    source       TEXT NOT NULL DEFAULT 'core'
                 CHECK (source IN ('core', 'builtin', 'extension', 'registry')),
    installed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_packages_type ON packages(type);
CREATE INDEX IF NOT EXISTS idx_packages_enabled ON packages(enabled) WHERE enabled = true;
CREATE INDEX IF NOT EXISTS idx_packages_team ON packages(team_id);
CREATE INDEX IF NOT EXISTS idx_packages_source ON packages(source);
CREATE INDEX IF NOT EXISTS idx_packages_status ON packages(status);

COMMENT ON TABLE packages IS 'Unified package registry. Surfaces, extensions, and full packages. Replaces surface_registry + extensions tables.';
COMMENT ON COLUMN packages.id IS 'Slug identifier from manifest "id" field. Used in URLs: /s/:id';
COMMENT ON COLUMN packages.type IS 'surface = routable page, extension = hooks/tools/pipes, full = both, workflow = bundled workflow definition';
COMMENT ON COLUMN packages.source IS 'core = page-engine seeded, builtin = extensions/builtin/ seeded, extension = admin-uploaded .pkg';
COMMENT ON COLUMN packages.enabled IS 'Admin toggle — disabled surfaces redirect to / and hide from nav';
COMMENT ON COLUMN packages.status IS 'Lifecycle: active (running), pending_review (needs admin permission grant), suspended (permission revoked)';


-- =========================================
-- PACKAGE USER SETTINGS
-- =========================================
-- Per-user overrides for packages (enable/disable, custom config).
-- Replaces extension_user_settings from the former extensions schema.
-- Lives in 016 (not 012) because of FK dependency on packages table.

CREATE TABLE IF NOT EXISTS package_user_settings (
    package_id   TEXT NOT NULL REFERENCES packages(id) ON DELETE CASCADE,
    user_id      UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    settings     JSONB   NOT NULL DEFAULT '{}',
    is_enabled   BOOLEAN NOT NULL DEFAULT true,
    PRIMARY KEY (package_id, user_id)
);


-- =========================================
-- EXTENSION PERMISSIONS
-- =========================================
-- Declared capabilities from package manifests. Admin grants control
-- which modules the Starlark sandbox injects at runtime.
-- v0.29.0: Permission model for extension sandboxing.

CREATE TABLE IF NOT EXISTS extension_permissions (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    package_id  TEXT NOT NULL REFERENCES packages(id) ON DELETE CASCADE,
    permission  TEXT NOT NULL,
    granted     BOOLEAN NOT NULL DEFAULT false,
    granted_by  UUID REFERENCES users(id) ON DELETE SET NULL,
    granted_at  TIMESTAMPTZ,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    UNIQUE(package_id, permission)
);

CREATE INDEX IF NOT EXISTS idx_ext_perm_package ON extension_permissions(package_id);
CREATE INDEX IF NOT EXISTS idx_ext_perm_granted ON extension_permissions(granted) WHERE granted = true;

COMMENT ON TABLE extension_permissions IS 'Declared permissions from package manifests. Admin grants control runtime module injection.';
COMMENT ON COLUMN extension_permissions.permission IS 'Capability key: secrets.read, notifications.send, filters.pre_completion, db.read, db.write, api.http';
COMMENT ON COLUMN extension_permissions.granted IS 'Admin has reviewed and approved this capability for the package';


-- =========================================
-- EXTENSION DATA TABLES CATALOG
-- =========================================
-- Tracks namespaced tables created for each extension on install.
-- Used by uninstall hooks (DROP TABLE) and db.list_tables().
-- Table names stored without the ext_{id}_ prefix.
-- v0.29.2: Extension data storage.

CREATE TABLE IF NOT EXISTS ext_data_tables (
    package_id  TEXT        NOT NULL REFERENCES packages(id) ON DELETE CASCADE,
    table_name  TEXT        NOT NULL,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    PRIMARY KEY (package_id, table_name)
);

CREATE INDEX IF NOT EXISTS idx_ext_data_tables_pkg ON ext_data_tables(package_id);

COMMENT ON TABLE ext_data_tables IS 'Catalog of namespaced tables created by extension install hooks. Drives uninstall cleanup and db.list_tables().';
COMMENT ON COLUMN ext_data_tables.table_name IS 'Logical name (without ext_{id}_ prefix). Physical table is ext_{package_id}_{table_name}.';


-- =========================================
-- PLATFORM VIEWS (extension read access)
-- =========================================
-- Column-allowlisted views over core platform tables.
-- Extensions query these via db.view("users"), db.view("channels").
-- Only safe, non-sensitive columns are exposed.
-- v0.29.2: Read contract for cross-platform data access.

CREATE OR REPLACE VIEW ext_view_users AS
    SELECT id, display_name, email FROM users;

CREATE OR REPLACE VIEW ext_view_channels AS
    SELECT id, title, type, team_id FROM channels;
