-- Authority Schema: Consolidated Initial Schema
-- Consolidated from migrations 001-005 (pre_1.0 archived)
-- Creates the complete authority schema for IAM, tenants, users, tokens, RLS, and audit

-- ============================================================================
-- SECTION 1: Schema Creation
-- ============================================================================

CREATE SCHEMA IF NOT EXISTS authority;
CREATE SCHEMA IF NOT EXISTS authority_app;

-- ============================================================================
-- SECTION 2: Helper Functions
-- ============================================================================

-- Function to update updated_at timestamp
CREATE OR REPLACE FUNCTION authority.update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Tenant context helper function for RLS
CREATE OR REPLACE FUNCTION authority_app.require_current_tenant()
RETURNS TEXT
LANGUAGE plpgsql STABLE SECURITY DEFINER
AS $$
DECLARE
    v_tenant TEXT;
BEGIN
    v_tenant := current_setting('app.tenant_id', true);
    IF v_tenant IS NULL OR v_tenant = '' THEN
        RAISE EXCEPTION 'app.tenant_id session variable not set'
            USING HINT = 'Set via: SELECT set_config(''app.tenant_id'', ''<tenant>'', false)',
                  ERRCODE = 'P0001';
    END IF;
    RETURN v_tenant;
END;
$$;

REVOKE ALL ON FUNCTION authority_app.require_current_tenant() FROM PUBLIC;

-- ============================================================================
-- SECTION 3: Core Tables
-- ============================================================================

-- Tenants table (NOT RLS-protected - defines tenant boundaries)
CREATE TABLE IF NOT EXISTS authority.tenants (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id TEXT NOT NULL UNIQUE,
    name TEXT NOT NULL,
    display_name TEXT,
    status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'deleted')),
    settings JSONB NOT NULL DEFAULT '{}',
    metadata JSONB NOT NULL DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by TEXT,
    updated_by TEXT
);

CREATE INDEX idx_tenants_status ON authority.tenants(status);
CREATE INDEX idx_tenants_created_at ON authority.tenants(created_at);

COMMENT ON TABLE authority.tenants IS
    'Tenant registry. Not RLS-protected - defines tenant boundaries for the system.';

-- Users table
CREATE TABLE IF NOT EXISTS authority.users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
    username TEXT NOT NULL,
    email TEXT,
    display_name TEXT,
    password_hash TEXT,
    password_salt TEXT,
    enabled BOOLEAN NOT NULL DEFAULT TRUE,
    password_algorithm TEXT DEFAULT 'argon2id',
    status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'locked', 'deleted')),
    email_verified BOOLEAN NOT NULL DEFAULT FALSE,
    mfa_enabled BOOLEAN NOT NULL DEFAULT FALSE,
    mfa_secret TEXT,
    mfa_backup_codes TEXT,
    failed_login_attempts INT NOT NULL DEFAULT 0,
    locked_until TIMESTAMPTZ,
    last_login_at TIMESTAMPTZ,
    password_changed_at TIMESTAMPTZ,
    last_password_change_at TIMESTAMPTZ,
    password_expires_at TIMESTAMPTZ,
    settings JSONB NOT NULL DEFAULT '{}',
    metadata JSONB NOT NULL DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by TEXT,
    updated_by TEXT,
    UNIQUE(tenant_id, username),
    UNIQUE(tenant_id, email)
);

CREATE INDEX idx_users_tenant_id ON authority.users(tenant_id);
CREATE INDEX idx_users_status ON authority.users(tenant_id, status);
CREATE INDEX idx_users_email ON authority.users(tenant_id, email);

-- Roles table
CREATE TABLE IF NOT EXISTS authority.roles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
    name TEXT NOT NULL,
    display_name TEXT,
    description TEXT,
    is_system BOOLEAN NOT NULL DEFAULT FALSE,
    metadata JSONB NOT NULL DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    UNIQUE(tenant_id, name)
);

CREATE INDEX idx_roles_tenant_id ON authority.roles(tenant_id);

-- Permissions table
CREATE TABLE IF NOT EXISTS authority.permissions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
    name TEXT NOT NULL,
    resource TEXT NOT NULL,
    action TEXT NOT NULL,
    description TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    UNIQUE(tenant_id, name)
);

CREATE INDEX idx_permissions_tenant_id ON authority.permissions(tenant_id);
CREATE INDEX idx_permissions_resource ON authority.permissions(tenant_id, resource);

-- Role-Permission assignments
CREATE TABLE IF NOT EXISTS authority.role_permissions (
    role_id UUID NOT NULL REFERENCES authority.roles(id) ON DELETE CASCADE,
    permission_id UUID NOT NULL REFERENCES authority.permissions(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    PRIMARY KEY (role_id, permission_id)
);

-- User-Role assignments
CREATE TABLE IF NOT EXISTS authority.user_roles (
    user_id UUID NOT NULL REFERENCES authority.users(id) ON DELETE CASCADE,
    role_id UUID NOT NULL REFERENCES authority.roles(id) ON DELETE CASCADE,
    granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    granted_by TEXT,
    expires_at TIMESTAMPTZ,
    PRIMARY KEY (user_id, role_id)
);

-- API Keys table
CREATE TABLE IF NOT EXISTS authority.api_keys (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
    user_id UUID REFERENCES authority.users(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    key_hash TEXT NOT NULL,
    key_prefix TEXT NOT NULL,
    scopes TEXT[] NOT NULL DEFAULT '{}',
    status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'revoked', 'expired')),
    last_used_at TIMESTAMPTZ,
    expires_at TIMESTAMPTZ,
    metadata JSONB NOT NULL DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revoked_at TIMESTAMPTZ,
    revoked_by TEXT
);

CREATE INDEX idx_api_keys_tenant_id ON authority.api_keys(tenant_id);
CREATE INDEX idx_api_keys_key_prefix ON authority.api_keys(key_prefix);
CREATE INDEX idx_api_keys_user_id ON authority.api_keys(user_id);
CREATE INDEX idx_api_keys_status ON authority.api_keys(tenant_id, status);

-- Tokens table (access tokens)
CREATE TABLE IF NOT EXISTS authority.tokens (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
    user_id UUID REFERENCES authority.users(id) ON DELETE CASCADE,
    token_hash TEXT NOT NULL UNIQUE,
    token_type TEXT NOT NULL DEFAULT 'access' CHECK (token_type IN ('access', 'refresh', 'api')),
    scopes TEXT[] NOT NULL DEFAULT '{}',
    client_id TEXT,
    issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMPTZ NOT NULL,
    revoked_at TIMESTAMPTZ,
    revoked_by TEXT,
    metadata JSONB NOT NULL DEFAULT '{}'
);

CREATE INDEX idx_tokens_tenant_id ON authority.tokens(tenant_id);
CREATE INDEX idx_tokens_user_id ON authority.tokens(user_id);
CREATE INDEX idx_tokens_expires_at ON authority.tokens(expires_at);
CREATE INDEX idx_tokens_token_hash ON authority.tokens(token_hash);

-- Refresh Tokens table
CREATE TABLE IF NOT EXISTS authority.refresh_tokens (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
    user_id UUID NOT NULL REFERENCES authority.users(id) ON DELETE CASCADE,
    token_hash TEXT NOT NULL UNIQUE,
    access_token_id UUID REFERENCES authority.tokens(id) ON DELETE SET NULL,
    client_id TEXT,
    issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMPTZ NOT NULL,
    revoked_at TIMESTAMPTZ,
    revoked_by TEXT,
    replaced_by UUID,
    metadata JSONB NOT NULL DEFAULT '{}'
);

CREATE INDEX idx_refresh_tokens_tenant_id ON authority.refresh_tokens(tenant_id);
CREATE INDEX idx_refresh_tokens_user_id ON authority.refresh_tokens(user_id);
CREATE INDEX idx_refresh_tokens_expires_at ON authority.refresh_tokens(expires_at);

-- Sessions table
CREATE TABLE IF NOT EXISTS authority.sessions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
    user_id UUID NOT NULL REFERENCES authority.users(id) ON DELETE CASCADE,
    session_token_hash TEXT NOT NULL UNIQUE,
    ip_address TEXT,
    user_agent TEXT,
    started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    last_activity_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    expires_at TIMESTAMPTZ NOT NULL,
    ended_at TIMESTAMPTZ,
    end_reason TEXT,
    metadata JSONB NOT NULL DEFAULT '{}'
);

CREATE INDEX idx_sessions_tenant_id ON authority.sessions(tenant_id);
CREATE INDEX idx_sessions_user_id ON authority.sessions(user_id);
CREATE INDEX idx_sessions_expires_at ON authority.sessions(expires_at);

-- Audit log table
CREATE TABLE IF NOT EXISTS authority.audit (
    id BIGSERIAL PRIMARY KEY,
    tenant_id TEXT NOT NULL,
    user_id UUID,
    action TEXT NOT NULL,
    resource_type TEXT NOT NULL,
    resource_id TEXT,
    old_value JSONB,
    new_value JSONB,
    ip_address TEXT,
    user_agent TEXT,
    correlation_id TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_audit_tenant_id ON authority.audit(tenant_id);
CREATE INDEX idx_audit_user_id ON authority.audit(user_id);
CREATE INDEX idx_audit_action ON authority.audit(action);
CREATE INDEX idx_audit_resource ON authority.audit(resource_type, resource_id);
CREATE INDEX idx_audit_created_at ON authority.audit(created_at);
CREATE INDEX idx_audit_correlation_id ON authority.audit(correlation_id);

-- ============================================================================
-- SECTION 4: OIDC and Mongo Store Equivalent Tables
-- ============================================================================

-- Bootstrap invites
CREATE TABLE IF NOT EXISTS authority.bootstrap_invites (
    id TEXT PRIMARY KEY,
    token TEXT NOT NULL UNIQUE,
    type TEXT NOT NULL,
    provider TEXT,
    target TEXT,
    expires_at TIMESTAMPTZ NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    issued_by TEXT,
    reserved_until TIMESTAMPTZ,
    reserved_by TEXT,
    consumed BOOLEAN NOT NULL DEFAULT FALSE,
    status TEXT NOT NULL DEFAULT 'pending',
    metadata JSONB NOT NULL DEFAULT '{}'
);

-- Service accounts
CREATE TABLE IF NOT EXISTS authority.service_accounts (
    id TEXT PRIMARY KEY,
    account_id TEXT NOT NULL UNIQUE,
    tenant TEXT NOT NULL,
    display_name TEXT NOT NULL,
    description TEXT,
    enabled BOOLEAN NOT NULL DEFAULT TRUE,
    allowed_scopes TEXT[] NOT NULL DEFAULT '{}',
    authorized_clients TEXT[] NOT NULL DEFAULT '{}',
    attributes JSONB NOT NULL DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_service_accounts_tenant ON authority.service_accounts(tenant);

-- Clients
CREATE TABLE IF NOT EXISTS authority.clients (
    id TEXT PRIMARY KEY,
    client_id TEXT NOT NULL UNIQUE,
    client_secret TEXT,
    secret_hash TEXT,
    display_name TEXT,
    description TEXT,
    plugin TEXT,
    sender_constraint TEXT,
    enabled BOOLEAN NOT NULL DEFAULT TRUE,
    redirect_uris TEXT[] NOT NULL DEFAULT '{}',
    post_logout_redirect_uris TEXT[] NOT NULL DEFAULT '{}',
    allowed_scopes TEXT[] NOT NULL DEFAULT '{}',
    allowed_grant_types TEXT[] NOT NULL DEFAULT '{}',
    require_client_secret BOOLEAN NOT NULL DEFAULT TRUE,
    require_pkce BOOLEAN NOT NULL DEFAULT FALSE,
    allow_plain_text_pkce BOOLEAN NOT NULL DEFAULT FALSE,
    client_type TEXT,
    properties JSONB NOT NULL DEFAULT '{}',
    certificate_bindings JSONB NOT NULL DEFAULT '[]',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Revocations
CREATE TABLE IF NOT EXISTS authority.revocations (
    id TEXT PRIMARY KEY,
    category TEXT NOT NULL,
    revocation_id TEXT NOT NULL,
    subject_id TEXT,
    client_id TEXT,
    token_id TEXT,
    reason TEXT NOT NULL,
    reason_description TEXT,
    revoked_at TIMESTAMPTZ NOT NULL,
    effective_at TIMESTAMPTZ NOT NULL,
    expires_at TIMESTAMPTZ,
    metadata JSONB NOT NULL DEFAULT '{}'
);

CREATE UNIQUE INDEX IF NOT EXISTS idx_revocations_category_revocation_id
    ON authority.revocations(category, revocation_id);

-- Login attempts
CREATE TABLE IF NOT EXISTS authority.login_attempts (
    id TEXT PRIMARY KEY,
    subject_id TEXT,
    client_id TEXT,
    event_type TEXT NOT NULL,
    outcome TEXT NOT NULL,
    reason TEXT,
    ip_address TEXT,
    user_agent TEXT,
    occurred_at TIMESTAMPTZ NOT NULL,
    properties JSONB NOT NULL DEFAULT '[]'
);

CREATE INDEX IF NOT EXISTS idx_login_attempts_subject ON authority.login_attempts(subject_id, occurred_at DESC);

-- OIDC tokens
CREATE TABLE IF NOT EXISTS authority.oidc_tokens (
    id TEXT PRIMARY KEY,
    token_id TEXT NOT NULL UNIQUE,
    subject_id TEXT,
    client_id TEXT,
    token_type TEXT NOT NULL,
    reference_id TEXT,
    created_at TIMESTAMPTZ NOT NULL,
    expires_at TIMESTAMPTZ,
    redeemed_at TIMESTAMPTZ,
    payload TEXT,
    properties JSONB NOT NULL DEFAULT '{}'
);

CREATE INDEX IF NOT EXISTS idx_oidc_tokens_subject ON authority.oidc_tokens(subject_id);
CREATE INDEX IF NOT EXISTS idx_oidc_tokens_client ON authority.oidc_tokens(client_id);
CREATE INDEX IF NOT EXISTS idx_oidc_tokens_reference ON authority.oidc_tokens(reference_id);

-- OIDC refresh tokens
CREATE TABLE IF NOT EXISTS authority.oidc_refresh_tokens (
    id TEXT PRIMARY KEY,
    token_id TEXT NOT NULL UNIQUE,
    subject_id TEXT,
    client_id TEXT,
    handle TEXT,
    created_at TIMESTAMPTZ NOT NULL,
    expires_at TIMESTAMPTZ,
    consumed_at TIMESTAMPTZ,
    payload TEXT
);

CREATE INDEX IF NOT EXISTS idx_oidc_refresh_tokens_subject ON authority.oidc_refresh_tokens(subject_id);
CREATE INDEX IF NOT EXISTS idx_oidc_refresh_tokens_handle ON authority.oidc_refresh_tokens(handle);

-- Airgap audit
CREATE TABLE IF NOT EXISTS authority.airgap_audit (
    id TEXT PRIMARY KEY,
    event_type TEXT NOT NULL,
    operator_id TEXT,
    component_id TEXT,
    outcome TEXT NOT NULL,
    reason TEXT,
    occurred_at TIMESTAMPTZ NOT NULL,
    properties JSONB NOT NULL DEFAULT '[]'
);

CREATE INDEX IF NOT EXISTS idx_airgap_audit_occurred_at ON authority.airgap_audit(occurred_at DESC);

-- Revocation export state (singleton row with optimistic concurrency)
CREATE TABLE IF NOT EXISTS authority.revocation_export_state (
    id INT PRIMARY KEY DEFAULT 1,
    sequence BIGINT NOT NULL DEFAULT 0,
    bundle_id TEXT,
    issued_at TIMESTAMPTZ
);

-- Offline Kit Audit
CREATE TABLE IF NOT EXISTS authority.offline_kit_audit (
    event_id UUID PRIMARY KEY,
    tenant_id TEXT NOT NULL,
    event_type TEXT NOT NULL,
    timestamp TIMESTAMPTZ NOT NULL,
    actor TEXT NOT NULL,
    details JSONB NOT NULL,
    result TEXT NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_offline_kit_audit_ts ON authority.offline_kit_audit(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_offline_kit_audit_type ON authority.offline_kit_audit(event_type);
CREATE INDEX IF NOT EXISTS idx_offline_kit_audit_tenant_ts ON authority.offline_kit_audit(tenant_id, timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_offline_kit_audit_result ON authority.offline_kit_audit(tenant_id, result, timestamp DESC);

-- Verdict manifests table
CREATE TABLE IF NOT EXISTS authority.verdict_manifests (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    manifest_id TEXT NOT NULL,
    tenant TEXT NOT NULL,
    asset_digest TEXT NOT NULL,
    vulnerability_id TEXT NOT NULL,
    inputs_json JSONB NOT NULL,
    status TEXT NOT NULL CHECK (status IN ('affected', 'not_affected', 'fixed', 'under_investigation')),
    confidence DOUBLE PRECISION NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
    result_json JSONB NOT NULL,
    policy_hash TEXT NOT NULL,
    lattice_version TEXT NOT NULL,
    evaluated_at TIMESTAMPTZ NOT NULL,
    manifest_digest TEXT NOT NULL,
    signature_base64 TEXT,
    rekor_log_id TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT uq_verdict_manifest_id UNIQUE (tenant, manifest_id)
);

CREATE INDEX IF NOT EXISTS idx_verdict_asset_vuln
    ON authority.verdict_manifests(tenant, asset_digest, vulnerability_id);
CREATE INDEX IF NOT EXISTS idx_verdict_policy
    ON authority.verdict_manifests(tenant, policy_hash, lattice_version);
CREATE INDEX IF NOT EXISTS idx_verdict_time
    ON authority.verdict_manifests USING BRIN (evaluated_at);
CREATE UNIQUE INDEX IF NOT EXISTS idx_verdict_replay
    ON authority.verdict_manifests(tenant, asset_digest, vulnerability_id, policy_hash, lattice_version);
CREATE INDEX IF NOT EXISTS idx_verdict_digest
    ON authority.verdict_manifests(manifest_digest);

COMMENT ON TABLE authority.verdict_manifests IS 'VEX verdict manifests for deterministic replay verification';

-- ============================================================================
-- SECTION 4b: Seed Default Tenant
-- ============================================================================

INSERT INTO authority.tenants (tenant_id, name, display_name, status)
VALUES ('demo-prod', 'Production', 'Demo Production', 'active')
ON CONFLICT (tenant_id) DO NOTHING;

-- ============================================================================
-- SECTION 5: Triggers
-- ============================================================================

CREATE TRIGGER trg_tenants_updated_at
    BEFORE UPDATE ON authority.tenants
    FOR EACH ROW EXECUTE FUNCTION authority.update_updated_at();

CREATE TRIGGER trg_users_updated_at
    BEFORE UPDATE ON authority.users
    FOR EACH ROW EXECUTE FUNCTION authority.update_updated_at();

CREATE TRIGGER trg_roles_updated_at
    BEFORE UPDATE ON authority.roles
    FOR EACH ROW EXECUTE FUNCTION authority.update_updated_at();

-- ============================================================================
-- SECTION 6: Row-Level Security
-- ============================================================================

-- authority.users
ALTER TABLE authority.users ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.users FORCE ROW LEVEL SECURITY;
CREATE POLICY users_tenant_isolation ON authority.users
    FOR ALL
    USING (tenant_id = authority_app.require_current_tenant())
    WITH CHECK (tenant_id = authority_app.require_current_tenant());

-- authority.roles
ALTER TABLE authority.roles ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.roles FORCE ROW LEVEL SECURITY;
CREATE POLICY roles_tenant_isolation ON authority.roles
    FOR ALL
    USING (tenant_id = authority_app.require_current_tenant())
    WITH CHECK (tenant_id = authority_app.require_current_tenant());

-- authority.permissions
ALTER TABLE authority.permissions ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.permissions FORCE ROW LEVEL SECURITY;
CREATE POLICY permissions_tenant_isolation ON authority.permissions
    FOR ALL
    USING (tenant_id = authority_app.require_current_tenant())
    WITH CHECK (tenant_id = authority_app.require_current_tenant());

-- authority.role_permissions (FK-based, inherits from roles)
ALTER TABLE authority.role_permissions ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.role_permissions FORCE ROW LEVEL SECURITY;
CREATE POLICY role_permissions_tenant_isolation ON authority.role_permissions
    FOR ALL
    USING (
        role_id IN (
            SELECT id FROM authority.roles
            WHERE tenant_id = authority_app.require_current_tenant()
        )
    );

-- authority.user_roles (FK-based, inherits from users)
ALTER TABLE authority.user_roles ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.user_roles FORCE ROW LEVEL SECURITY;
CREATE POLICY user_roles_tenant_isolation ON authority.user_roles
    FOR ALL
    USING (
        user_id IN (
            SELECT id FROM authority.users
            WHERE tenant_id = authority_app.require_current_tenant()
        )
    );

-- authority.api_keys
ALTER TABLE authority.api_keys ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.api_keys FORCE ROW LEVEL SECURITY;
CREATE POLICY [REDACTED]ON authority.api_keys
    FOR ALL
    USING (tenant_id = authority_app.require_current_tenant())
    WITH CHECK (tenant_id = authority_app.require_current_tenant());

-- authority.tokens
ALTER TABLE authority.tokens ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.tokens FORCE ROW LEVEL SECURITY;
CREATE POLICY tokens_tenant_isolation ON authority.tokens
    FOR ALL
    USING (tenant_id = authority_app.require_current_tenant())
    WITH CHECK (tenant_id = authority_app.require_current_tenant());

-- authority.refresh_tokens
ALTER TABLE authority.refresh_tokens ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.refresh_tokens FORCE ROW LEVEL SECURITY;
CREATE POLICY refresh_tokens_tenant_isolation ON authority.refresh_tokens
    FOR ALL
    USING (tenant_id = authority_app.require_current_tenant())
    WITH CHECK (tenant_id = authority_app.require_current_tenant());

-- authority.sessions
ALTER TABLE authority.sessions ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.sessions FORCE ROW LEVEL SECURITY;
CREATE POLICY sessions_tenant_isolation ON authority.sessions
    FOR ALL
    USING (tenant_id = authority_app.require_current_tenant())
    WITH CHECK (tenant_id = authority_app.require_current_tenant());

-- authority.audit
ALTER TABLE authority.audit ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.audit FORCE ROW LEVEL SECURITY;
CREATE POLICY audit_tenant_isolation ON authority.audit
    FOR ALL
    USING (tenant_id = authority_app.require_current_tenant())
    WITH CHECK (tenant_id = authority_app.require_current_tenant());

-- authority.offline_kit_audit
ALTER TABLE authority.offline_kit_audit ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.offline_kit_audit FORCE ROW LEVEL SECURITY;
CREATE POLICY offline_kit_audit_tenant_isolation ON authority.offline_kit_audit
    FOR ALL
    USING (tenant_id = authority_app.require_current_tenant())
    WITH CHECK (tenant_id = authority_app.require_current_tenant());

-- authority.verdict_manifests
ALTER TABLE authority.verdict_manifests ENABLE ROW LEVEL SECURITY;
CREATE POLICY verdict_tenant_isolation ON authority.verdict_manifests
    USING (tenant = current_setting('app.current_tenant', true))
    WITH CHECK (tenant = current_setting('app.current_tenant', true));

-- ============================================================================
-- SECTION 7: Roles and Permissions
-- ============================================================================

DO $$
BEGIN
    IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'authority_admin') THEN
        CREATE ROLE authority_admin WITH NOLOGIN BYPASSRLS;
    END IF;
END
$$;

-- Grant permissions (if role exists)
DO $$
BEGIN
    IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'stellaops_app') THEN
        GRANT SELECT, INSERT, UPDATE, DELETE ON authority.verdict_manifests TO stellaops_app;
        GRANT USAGE ON SCHEMA authority TO stellaops_app;
    END IF;
END
$$;

-- ============================================================================
-- SECTION 8: Demo Seed Data
-- ============================================================================

-- Roles for demo-prod tenant
INSERT INTO authority.roles (id, tenant_id, name, display_name, description, is_system)
VALUES
    ('a0000002-0000-0000-0000-000000000001', 'demo-prod', 'admin', 'Administrator', 'Full platform access', true),
    ('a0000002-0000-0000-0000-000000000002', 'demo-prod', 'operator', 'Operator', 'Release and deployment operations', true),
    ('a0000002-0000-0000-0000-000000000003', 'demo-prod', 'viewer', 'Viewer', 'Read-only access', true)
ON CONFLICT (tenant_id, name) DO NOTHING;

-- OAuth Clients
INSERT INTO authority.clients (id, client_id, display_name, description, enabled, redirect_uris, post_logout_redirect_uris, allowed_scopes, allowed_grant_types, require_client_secret, require_pkce, properties)
VALUES
    ('demo-client-ui', 'stella-ops-ui', 'Stella Ops Console', 'Web UI application', true,
     ARRAY['https://stella-ops.local/auth/callback', 'https://stella-ops.local/auth/silent-refresh', 'https://127.1.0.1/auth/callback', 'https://127.1.0.1/auth/silent-refresh'],
     ARRAY['https://stella-ops.local/', 'https://127.1.0.1/'],
     ARRAY['openid', 'profile', 'email', 'offline_access',
            'ui.read', 'ui.admin', 'ui.preferences.read', 'ui.preferences.write',
            'authority:tenants.read', 'authority:tenants.write',
            'authority:users.read', 'authority:users.write',
            'authority:roles.read', 'authority:roles.write',
            'authority:clients.read', 'authority:clients.write',
            'authority:tokens.read', 'authority:tokens.revoke',
            'authority:branding.read', 'authority:branding.write',
            'authority.audit.read',
            'graph:read', 'sbom:read', 'scanner:read',
            'policy:read', 'policy:simulate', 'policy:author', 'policy:review', 'policy:approve',
            'policy:run', 'policy:activate', 'policy:audit', 'policy:edit', 'policy:operate', 'policy:publish',
            'airgap:seal', 'airgap:status:read',
            'orch:read', 'analytics.read', 'advisory:read', 'advisory-ai:view', 'advisory-ai:operate',
            'vex:read', 'vexhub:read',
            'exceptions:read', 'exceptions:approve', 'aoc:verify', 'findings:read',
            'release:read', 'release:write', 'release:publish', 'scheduler:read', 'scheduler:operate',
            'notify.viewer', 'notify.operator', 'notify.admin', 'notify.escalate',
            'evidence:read',
            'export.viewer', 'export.operator', 'export.admin',
            'vuln:view', 'vuln:investigate', 'vuln:operate', 'vuln:audit',
            'platform.context.read', 'platform.context.write',
            'doctor:run', 'doctor:admin', 'ops.health',
            'integration:read', 'integration:write', 'integration:operate', 'registry.admin',
            'timeline:read', 'timeline:write',
            'signer:read', 'signer:sign', 'signer:rotate', 'signer:admin',
            'trust:read', 'trust:write', 'trust:admin'],
     ARRAY['authorization_code', 'refresh_token'],
     false, true, '{"tenant": "demo-prod"}'::jsonb)
ON CONFLICT (client_id) DO NOTHING;
