-- IAM (Identity and Access Management) schema
-- Purpose: 多角色权限系统核心表
-- Updated: 2026-04-03
-- Dependencies: platform_core_schema_v1.sql (core_tenants, iam_users)

BEGIN;

-- 角色表 (iam_roles)
CREATE TABLE IF NOT EXISTS iam_roles (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    code VARCHAR(32) NOT NULL UNIQUE,
    name VARCHAR(128) NOT NULL,
    type VARCHAR(20) NOT NULL DEFAULT 'platform'
        CHECK (type IN ('platform', 'supply', 'consumer')),
    parent_role_id BIGINT REFERENCES iam_roles(id),
    level INT NOT NULL DEFAULT 0,
    description TEXT,
    is_active BOOLEAN NOT NULL DEFAULT TRUE,

    -- 审计字段
    request_id VARCHAR(64),
    created_ip INET,
    updated_ip INET,
    version INT NOT NULL DEFAULT 1,

    -- 时间戳
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- 约束
    CONSTRAINT chk_role_level_non_negative CHECK (level >= 0),
    CONSTRAINT chk_role_code_format CHECK (code ~ '^[a-z][a-z0-9_]{0,31}$')
);

CREATE INDEX IF NOT EXISTS idx_iam_roles_code ON iam_roles (code);
CREATE INDEX IF NOT EXISTS idx_iam_roles_type ON iam_roles (type);
CREATE INDEX IF NOT EXISTS idx_iam_roles_parent ON iam_roles (parent_role_id);
CREATE INDEX IF NOT EXISTS idx_iam_roles_level ON iam_roles (level);
CREATE INDEX IF NOT EXISTS idx_iam_roles_active ON iam_roles (is_active);

-- Scope权限表 (iam_scopes)
CREATE TABLE IF NOT EXISTS iam_scopes (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    code VARCHAR(64) NOT NULL UNIQUE,
    name VARCHAR(128) NOT NULL,
    description TEXT,
    category VARCHAR(32) NOT NULL DEFAULT 'generic'
        CHECK (category IN ('generic', 'billing', 'audit', 'iam', 'gateway')),
    is_active BOOLEAN NOT NULL DEFAULT TRUE,

    -- 审计字段
    request_id VARCHAR(64),
    version INT NOT NULL DEFAULT 1,

    -- 时间戳
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- 约束
    CONSTRAINT chk_scope_code_format CHECK (code ~ '^[a-z][a-z0-9._]{0,63}$')
);

CREATE INDEX IF NOT EXISTS idx_iam_scopes_code ON iam_scopes (code);
CREATE INDEX IF NOT EXISTS idx_iam_scopes_category ON iam_scopes (category);
CREATE INDEX IF NOT EXISTS idx_iam_scopes_active ON iam_scopes (is_active);

-- 角色-Scope关联表 (iam_role_scopes)
CREATE TABLE IF NOT EXISTS iam_role_scopes (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    role_id BIGINT NOT NULL REFERENCES iam_roles(id) ON DELETE CASCADE,
    scope_id BIGINT NOT NULL REFERENCES iam_scopes(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- 约束：唯一索引防止重复
    UNIQUE (role_id, scope_id)
);

CREATE INDEX IF NOT EXISTS idx_iam_role_scopes_role ON iam_role_scopes (role_id);
CREATE INDEX IF NOT EXISTS idx_iam_role_scopes_scope ON iam_role_scopes (scope_id);

-- 用户-角色关联表 (iam_user_roles)
CREATE TABLE IF NOT EXISTS iam_user_roles (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    user_id BIGINT NOT NULL REFERENCES iam_users(id) ON DELETE CASCADE,
    role_id BIGINT NOT NULL REFERENCES iam_roles(id) ON DELETE CASCADE,
    tenant_id BIGINT REFERENCES core_tenants(id),
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    granted_by BIGINT REFERENCES iam_users(id),
    expires_at TIMESTAMPTZ,

    -- 审计字段
    request_id VARCHAR(64),
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- 约束：唯一索引
    UNIQUE (user_id, role_id, tenant_id)
);

CREATE INDEX IF NOT EXISTS idx_iam_user_roles_user ON iam_user_roles (user_id);
CREATE INDEX IF NOT EXISTS idx_iam_user_roles_role ON iam_user_roles (role_id);
CREATE INDEX IF NOT EXISTS idx_iam_user_roles_tenant ON iam_user_roles (tenant_id);
CREATE INDEX IF NOT EXISTS idx_iam_user_roles_active ON iam_user_roles (is_active);
CREATE INDEX IF NOT EXISTS idx_iam_user_roles_expires ON iam_user_roles (expires_at) WHERE expires_at IS NOT NULL;

-- 角色继承关系表 (iam_role_hierarchy)
-- 用于支持角色的继承关系，如 org_admin 继承自 super_admin
CREATE TABLE IF NOT EXISTS iam_role_hierarchy (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    child_role_id BIGINT NOT NULL REFERENCES iam_roles(id) ON DELETE CASCADE,
    parent_role_id BIGINT NOT NULL REFERENCES iam_roles(id) ON DELETE CASCADE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- 约束：唯一索引
    UNIQUE (child_role_id, parent_role_id),
    -- 约束：防止自引用
    CONSTRAINT chk_no_self_reference CHECK (child_role_id != parent_role_id)
);

CREATE INDEX IF NOT EXISTS idx_iam_role_hierarchy_child ON iam_role_hierarchy (child_role_id);
CREATE INDEX IF NOT EXISTS idx_iam_role_hierarchy_parent ON iam_role_hierarchy (parent_role_id);

-- 插入默认角色数据
INSERT INTO iam_roles (code, name, type, level, description, is_active) VALUES
    ('super_admin', '超级管理员', 'platform', 100, '平台超级管理员，拥有所有权限', TRUE),
    ('org_admin', '组织管理员', 'platform', 50, '组织管理员，管理整个组织', TRUE),
    ('supply_admin', '供应管理员', 'supply', 40, '供应管理员，管理供应链', TRUE),
    ('operator', '运营人员', 'platform', 30, '运营人员，执行日常操作', TRUE),
    ('developer', '开发人员', 'platform', 20, '开发人员，访问开发资源', TRUE),
    ('finops', '财务人员', 'platform', 20, '财务人员，访问账单和报表', TRUE),
    ('viewer', '只读用户', 'platform', 10, '只读用户，仅能查看资源', TRUE)
ON CONFLICT (code) DO NOTHING;

-- 插入默认Scope数据
INSERT INTO iam_scopes (code, name, category, description) VALUES
    ('*', '全部权限', 'generic', '超级管理员拥有的全部权限'),
    ('gateway.invoke', '网关调用', 'gateway', '调用网关API'),
    ('gateway.read', '网关读取', 'gateway', '读取网关配置'),
    ('gateway.write', '网关写入', 'gateway', '修改网关配置'),
    ('billing.read', '账单读取', 'billing', '读取账单信息'),
    ('billing.write', '账单写入', 'billing', '修改账单设置'),
    ('audit.read', '审计读取', 'audit', '读取审计日志'),
    ('audit.write', '审计写入', 'audit', '创建审计事件'),
    ('iam.read', 'IAM读取', 'iam', '读取IAM配置'),
    ('iam.write', 'IAM写入', 'iam', '修改IAM配置'),
    ('iam.admin', 'IAM管理', 'iam', '管理IAM所有设置')
ON CONFLICT (code) DO NOTHING;

-- 为超级管理员角色分配全部权限
INSERT INTO iam_role_scopes (role_id, scope_id)
SELECT r.id, s.id FROM iam_roles r, iam_scopes s
WHERE r.code = 'super_admin' AND s.code = '*'
ON CONFLICT DO NOTHING;

-- 为组织管理员分配主要管理权限
INSERT INTO iam_role_scopes (role_id, scope_id)
SELECT r.id, s.id FROM iam_roles r, iam_scopes s
WHERE r.code = 'org_admin' AND s.code IN ('gateway.invoke', 'gateway.read', 'billing.read', 'audit.read', 'iam.read')
ON CONFLICT DO NOTHING;

COMMIT;

-- 注释说明
COMMENT ON TABLE iam_roles IS '角色定义表，存储系统中的所有角色';
COMMENT ON TABLE iam_scopes IS '权限范围表，定义细粒度的权限';
COMMENT ON TABLE iam_role_scopes IS '角色与权限的关联表';
COMMENT ON TABLE iam_user_roles IS '用户与角色的关联表';
COMMENT ON TABLE iam_role_hierarchy IS '角色继承关系表';
