<template>
  <el-card shadow="never" class="rbac-card">
    <template #header>
      <div class="rbac-header">
        <div class="rbac-title-wrap">
          <span class="rbac-title">权限与角色管理</span>
          <span class="rbac-desc">为角色分配可访问页面、操作与控件，并为用户指派角色（标准 RBAC）。</span>
        </div>
        <el-button :icon="Refresh" :loading="loading" @click="reloadAll">刷新</el-button>
      </div>
    </template>

    <el-tabs v-model="activeTab" class="rbac-tabs">
      <!-- ───────────────────────── 用户 ───────────────────────── -->
      <el-tab-pane label="用户管理" name="users">
        <div class="tab-toolbar">
          <span class="toolbar-hint">共 {{ users.length }} 个用户</span>
          <el-button type="primary" :icon="Plus" @click="openCreateUser">新增用户</el-button>
        </div>
        <el-table :data="users" v-loading="loading" size="default" border stripe>
          <el-table-column label="用户名" prop="username" width="140" />
          <el-table-column label="显示名" prop="displayName" width="150" />
          <el-table-column label="角色" min-width="200">
            <template #default="{ row }">
              <el-tag v-for="rid in row.roleIds" :key="rid" size="small" effect="plain" class="mg-tag">{{ roleName(rid) }}</el-tag>
              <span v-if="!row.roleIds.length" class="muted">—</span>
            </template>
          </el-table-column>
          <el-table-column label="可用域" width="180">
            <template #default="{ row }">
              <el-tag v-for="s in row.scopes" :key="s" size="small" type="info" effect="plain" class="mg-tag">{{ scopeLabel(s) }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column label="启用" width="90" align="center">
            <template #default="{ row }">
              <el-switch :model-value="row.enabled" :loading="row._busy" @change="(v: string | number | boolean) => toggleEnabled(row, Boolean(v))" />
            </template>
          </el-table-column>
          <el-table-column label="操作" width="220" align="center">
            <template #default="{ row }">
              <el-button link type="primary" :icon="Edit" @click="openEditUser(row)">编辑</el-button>
              <el-button link type="warning" :icon="Key" @click="openResetPassword(row)">改密</el-button>
              <el-button link type="danger" :icon="Delete" :disabled="row.id === selfId" @click="removeUser(row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-tab-pane>

      <!-- ───────────────────────── 角色 ───────────────────────── -->
      <el-tab-pane label="角色与页面权限" name="roles">
        <div class="tab-toolbar">
          <span class="toolbar-hint">共 {{ roles.length }} 个角色</span>
          <el-button type="primary" :icon="Plus" @click="openCreateRole">新增角色</el-button>
        </div>
        <el-table :data="roles" v-loading="loading" size="default" border stripe>
          <el-table-column label="角色名称" width="160">
            <template #default="{ row }">
              <span>{{ row.name }}</span>
              <el-tag v-if="row.system" size="small" type="success" effect="plain" class="mg-tag">内置</el-tag>
            </template>
          </el-table-column>
          <el-table-column label="描述" prop="description" min-width="180" show-overflow-tooltip />
          <el-table-column label="归属域" width="120">
            <template #default="{ row }"><el-tag size="small" :type="scopeTag(row.scope)" effect="plain">{{ scopeLabel(row.scope) }}</el-tag></template>
          </el-table-column>
          <el-table-column label="可访问页面" width="130" align="center">
            <template #default="{ row }">
              <el-tag v-if="row.pages.includes('*')" size="small" type="danger" effect="plain">全部页面</el-tag>
              <span v-else>{{ row.pages.length }} 个</span>
            </template>
          </el-table-column>
          <el-table-column label="操作" width="160" align="center">
            <template #default="{ row }">
              <el-button link type="primary" :icon="Edit" @click="openEditRole(row)">编辑</el-button>
              <el-button link type="danger" :icon="Delete" :disabled="row.system" @click="removeRole(row)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </el-tab-pane>
    </el-tabs>
  </el-card>

  <!-- ───────────────────────── 用户 Dialog ───────────────────────── -->
  <el-dialog v-model="userDialog" :title="userForm.id ? '编辑用户' : '新增用户'" width="480px" destroy-on-close>
    <el-form label-width="84px" label-position="right">
      <el-form-item label="用户名" required>
        <el-input v-model="userForm.username" :disabled="!!userForm.id" placeholder="登录用户名" />
      </el-form-item>
      <el-form-item label="显示名">
        <el-input v-model="userForm.displayName" placeholder="界面展示名称（留空则同用户名）" />
      </el-form-item>
      <el-form-item v-if="!userForm.id" label="初始密码" required>
        <el-input v-model="userForm.password" type="password" show-password placeholder="设置初始登录密码" />
      </el-form-item>
      <el-form-item label="角色">
        <el-select v-model="userForm.roleIds" multiple filterable placeholder="选择一个或多个角色" style="width: 100%">
          <el-option v-for="r in roles" :key="r.id" :label="`${r.name}（${scopeLabel(r.scope)}）`" :value="r.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="启用">
        <el-switch v-model="userForm.enabled" />
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="userDialog = false">取消</el-button>
      <el-button type="primary" :loading="saving" @click="submitUser">保存</el-button>
    </template>
  </el-dialog>

  <!-- ───────────────────────── 改密 Dialog ───────────────────────── -->
  <el-dialog v-model="pwdDialog" title="重置密码" width="420px" destroy-on-close>
    <el-form label-width="84px">
      <el-form-item label="用户">
        <span>{{ pwdTarget?.displayName }}（{{ pwdTarget?.username }}）</span>
      </el-form-item>
      <el-form-item label="新密码" required>
        <el-input v-model="newPassword" type="password" show-password placeholder="输入新密码" />
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="pwdDialog = false">取消</el-button>
      <el-button type="primary" :loading="saving" @click="submitPassword">确定</el-button>
    </template>
  </el-dialog>

  <!-- ───────────────────────── 角色 Dialog ───────────────────────── -->
  <el-dialog v-model="roleDialog" :title="roleForm.id ? '编辑角色' : '新增角色'" width="760px" top="6vh" destroy-on-close>
    <el-form label-width="92px" label-position="right">
      <el-row :gutter="12">
        <el-col :span="12">
          <el-form-item label="角色名称" required>
            <el-input v-model="roleForm.name" placeholder="如：场景设计员" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="归属域" required>
            <el-select v-model="roleForm.scope" style="width: 100%" @change="onScopeChange">
              <el-option v-for="s in catalog?.scopes ?? []" :key="s.value" :label="s.label" :value="s.value" />
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
      <el-form-item label="描述">
        <el-input v-model="roleForm.description" placeholder="角色用途说明" />
      </el-form-item>

      <el-divider content-position="left">可访问页面</el-divider>
      <el-form-item label="授权方式">
        <el-switch v-model="wildcardPages" active-text="该域全部页面（含未来新增）" inactive-text="按页面精确勾选" inline-prompt style="--el-switch-on-color: var(--mg-primary, #6366f1)" />
      </el-form-item>
      <div v-if="!wildcardPages" class="page-picker">
        <div v-for="g in groupedPages" :key="g.group" class="page-group">
          <div class="page-group-head">
            <span class="page-group-title">{{ g.group }}</span>
            <span class="page-group-actions">
              <el-button link type="primary" size="small" @click="selectGroup(g.pages, true)">全选</el-button>
              <el-button link type="info" size="small" @click="selectGroup(g.pages, false)">清空</el-button>
            </span>
          </div>
          <el-checkbox-group v-model="selectedPages" class="page-checks">
            <el-checkbox v-for="p in g.pages" :key="p.key" :value="p.key" :label="p.key">{{ p.label }}</el-checkbox>
          </el-checkbox-group>
        </div>
        <el-empty v-if="!groupedPages.length" description="该域暂无可分配页面" :image-size="60" />
      </div>
      <div v-else class="wildcard-hint">已授予「{{ scopeLabel(roleForm.scope) }}」下的全部页面（包括未来新增的页面）。</div>
    </el-form>
    <template #footer>
      <el-button @click="roleDialog = false">取消</el-button>
      <el-button type="primary" :loading="saving" @click="submitRole">保存</el-button>
    </template>
  </el-dialog>
</template>

<script setup lang="ts">
import { computed, onMounted, reactive, ref } from 'vue'
import { Plus, Edit, Delete, Key, Refresh } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useAuthStore } from '@/stores/auth'
import {
  fetchCatalog, fetchRoles, fetchUsers,
  saveRole, deleteRole, createUser, updateUser, setUserPassword, deleteUser
} from '@/api/rbac'
import type { RbacRole, RbacUserView, RbacCatalog, PageDef, RbacScope } from '@/types/rbac'
import type { WidgetGrant } from '@/types/auth'

const auth = useAuthStore()
const selfId = computed(() => auth.user?.id ?? '')

const activeTab = ref<'users' | 'roles'>('users')
const loading = ref(false)
const saving = ref(false)

const catalog = ref<RbacCatalog | null>(null)
const roles = ref<RbacRole[]>([])
const users = ref<Array<RbacUserView & { _busy?: boolean }>>([])

function roleName(id: string): string {
  return roles.value.find((r) => r.id === id)?.name ?? id
}
function scopeLabel(scope: string): string {
  return scope === '*' ? '通用' : scope === 'Platform' ? '管理端' : scope === 'RCSMonitor' ? '运营端' : scope
}
function scopeTag(scope: string): 'success' | 'warning' | 'danger' | 'info' {
  return scope === '*' ? 'danger' : scope === 'Platform' ? 'warning' : 'success'
}

async function reloadAll() {
  loading.value = true
  try {
    const [cat, rs, us] = await Promise.all([fetchCatalog(), fetchRoles(), fetchUsers()])
    catalog.value = cat
    roles.value = rs
    users.value = us
  } catch (e) {
    ElMessage.error(`加载失败：${msg(e)}`)
  } finally {
    loading.value = false
  }
}

// ───────────────────────── 用户 ─────────────────────────
const userDialog = ref(false)
const userForm = reactive<{ id: string | null; username: string; displayName: string; password: string; roleIds: string[]; enabled: boolean }>({
  id: null, username: '', displayName: '', password: '', roleIds: [], enabled: true
})

function openCreateUser() {
  Object.assign(userForm, { id: null, username: '', displayName: '', password: '', roleIds: [], enabled: true })
  userDialog.value = true
}
function openEditUser(row: RbacUserView) {
  Object.assign(userForm, { id: row.id, username: row.username, displayName: row.displayName, password: '', roleIds: [...row.roleIds], enabled: row.enabled })
  userDialog.value = true
}
async function submitUser() {
  saving.value = true
  try {
    if (userForm.id) {
      await updateUser(userForm.id, { displayName: userForm.displayName, roleIds: userForm.roleIds, enabled: userForm.enabled })
      ElMessage.success('用户已更新')
    } else {
      await createUser({ username: userForm.username, displayName: userForm.displayName, password: userForm.password, roleIds: userForm.roleIds, enabled: userForm.enabled })
      ElMessage.success('用户已创建')
    }
    userDialog.value = false
    await reloadAll()
  } catch (e) {
    ElMessage.error(msg(e))
  } finally {
    saving.value = false
  }
}
async function toggleEnabled(row: RbacUserView & { _busy?: boolean }, val: boolean) {
  row._busy = true
  try {
    await updateUser(row.id, { enabled: val })
    row.enabled = val
    ElMessage.success(val ? '已启用' : '已停用')
  } catch (e) {
    ElMessage.error(msg(e))
  } finally {
    row._busy = false
  }
}
async function removeUser(row: RbacUserView) {
  try {
    await ElMessageBox.confirm(`确定删除用户「${row.username}」？此操作不可恢复。`, '删除确认', { type: 'warning' })
  } catch { return }
  try {
    await deleteUser(row.id)
    ElMessage.success('用户已删除')
    await reloadAll()
  } catch (e) {
    ElMessage.error(msg(e))
  }
}

// 改密
const pwdDialog = ref(false)
const pwdTarget = ref<RbacUserView | null>(null)
const newPassword = ref('')
function openResetPassword(row: RbacUserView) {
  pwdTarget.value = row
  newPassword.value = ''
  pwdDialog.value = true
}
async function submitPassword() {
  if (!pwdTarget.value) return
  if (!newPassword.value) { ElMessage.warning('请输入新密码'); return }
  saving.value = true
  try {
    await setUserPassword(pwdTarget.value.id, newPassword.value)
    ElMessage.success('密码已重置')
    pwdDialog.value = false
  } catch (e) {
    ElMessage.error(msg(e))
  } finally {
    saving.value = false
  }
}

// ───────────────────────── 角色 ─────────────────────────
const roleDialog = ref(false)
const roleForm = reactive<{ id: string | null; name: string; description: string; scope: RbacScope }>({
  id: null, name: '', description: '', scope: 'Platform'
})
const selectedPages = ref<string[]>([])
const wildcardPages = ref(false)
// 操作权限 / 控件可见性不在本界面暴露编辑：编辑时记录原值、保存时原样回传，
// 避免把内置角色（如运营）已有的运维操作码 / 控件可见性配置清空。
const preservedOps = ref<string[]>([])
const preservedWidgets = ref<WidgetGrant[]>([])

const availablePages = computed<PageDef[]>(() =>
  (catalog.value?.pages ?? []).filter((p) => roleForm.scope === '*' || p.scope === roleForm.scope))

const groupedPages = computed(() => {
  const map = new Map<string, PageDef[]>()
  for (const p of availablePages.value) {
    if (!map.has(p.group)) map.set(p.group, [])
    map.get(p.group)!.push(p)
  }
  return [...map.entries()].map(([group, pages]) => ({ group, pages }))
})

function openCreateRole() {
  Object.assign(roleForm, { id: null, name: '', description: '', scope: 'Platform' })
  selectedPages.value = []
  wildcardPages.value = false
  preservedOps.value = []
  preservedWidgets.value = []
  roleDialog.value = true
}
function openEditRole(row: RbacRole) {
  Object.assign(roleForm, { id: row.id, name: row.name, description: row.description, scope: row.scope })
  wildcardPages.value = row.pages.includes('*')
  selectedPages.value = wildcardPages.value ? [] : [...row.pages]
  preservedOps.value = [...row.ops]
  preservedWidgets.value = row.widgetGrants.map((g) => ({ ...g }))
  roleDialog.value = true
}
function onScopeChange() {
  // 切换归属域后，剔除不属于新域的已选页面
  const valid = new Set(availablePages.value.map((p) => p.key))
  selectedPages.value = selectedPages.value.filter((k) => valid.has(k))
}
function selectGroup(pages: PageDef[], on: boolean) {
  const keys = pages.map((p) => p.key)
  if (on) selectedPages.value = [...new Set([...selectedPages.value, ...keys])]
  else selectedPages.value = selectedPages.value.filter((k) => !keys.includes(k))
}
async function submitRole() {
  if (!roleForm.name.trim()) { ElMessage.warning('请填写角色名称'); return }
  saving.value = true
  try {
    await saveRole({
      id: roleForm.id ?? undefined,
      name: roleForm.name,
      description: roleForm.description,
      scope: roleForm.scope,
      pages: wildcardPages.value ? ['*'] : selectedPages.value,
      ops: preservedOps.value,
      widgetGrants: preservedWidgets.value
    })
    ElMessage.success(roleForm.id ? '角色已更新' : '角色已创建')
    roleDialog.value = false
    await reloadAll()
  } catch (e) {
    ElMessage.error(msg(e))
  } finally {
    saving.value = false
  }
}
async function removeRole(row: RbacRole) {
  try {
    await ElMessageBox.confirm(`确定删除角色「${row.name}」？`, '删除确认', { type: 'warning' })
  } catch { return }
  try {
    await deleteRole(row.id)
    ElMessage.success('角色已删除')
    await reloadAll()
  } catch (e) {
    ElMessage.error(msg(e))
  }
}

function msg(e: unknown): string {
  return e instanceof Error ? e.message : String(e)
}

onMounted(reloadAll)
</script>

<style scoped>
.rbac-header { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.rbac-title-wrap { display: flex; flex-direction: column; gap: 4px; }
.rbac-title { font-weight: 600; font-size: 16px; color: var(--mg-text-light); }
.rbac-desc { font-size: 12.5px; color: var(--mg-text-muted); }
.tab-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.toolbar-hint { font-size: 13px; color: var(--mg-text-muted); }
.mg-tag { margin: 2px 4px 2px 0; }
.muted { color: var(--mg-text-faint); }

.page-picker { display: flex; flex-direction: column; gap: 14px; max-height: 320px; overflow-y: auto; padding: 4px 2px; }
.page-group-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
.page-group-title { font-weight: 600; font-size: 13px; color: var(--mg-accent, #c4b5fd); }
.page-checks { display: flex; flex-wrap: wrap; gap: 4px 18px; }
.wildcard-hint { font-size: 13px; color: var(--mg-status-warning, #f59e0b); padding: 4px 0 8px; }
</style>
