<template>
  <div
    class="ivyforms-settings-permissions ivyforms-flex ivyforms-flex-1 ivyforms-flex-direction-column ivyforms-pr-20 ivyforms-min-height-0 ivyforms-gap-24"
  >
    <div
      class="ivyforms-settings-permissions__option-bar ivyforms-flex ivyforms-flex-shrink-0 ivyforms-justify-content-between ivyforms-align-items-center"
    >
      <span class="medium-18">{{ getLabel('permissions') }}</span>
      <IvyButtonAction priority="primary" type="fill" @click="openAddModal">
        {{ getLabel('add_permission') }}
      </IvyButtonAction>
    </div>

    <div
      class="ivyforms-settings-permissions__table ivyforms-flex ivyforms-flex-1 ivyforms-flex-direction-column ivyforms-min-height-0"
    >
      <span class="medium-18 ivyforms-pb-12">{{ getLabel('role_based_permissions') }}</span>
      <IvyTable
        ref="tableRef"
        pagination
        :data="shouldShowSkeleton ? [] : tableData"
        :loading="shouldShowSkeleton"
        :total="total"
        :row-key="'id'"
        :row-height="permissionsRowHeight"
        :columns="dynamicWidthColumns"
        :min-table-width="getMinTableWidth"
      >
        <template #empty>
          <div v-if="shouldShowSkeleton" class="ivyforms-table-skeleton">
            <IvySkeleton>
              <template #template>
                <IvySkeletonItem
                  v-for="n in pagination.perPage"
                  :key="n"
                  class="ivyforms-skeleton-row"
                />
              </template>
            </IvySkeleton>
          </div>
          <PageEmptyState
            v-else
            image="not-found"
            :title="getLabel('permissions')"
            :subtitle="getLabel('permissions_empty')"
          >
            <template #actionButton>
              <IvyButtonAction priority="primary" type="fill" @click="openAddModal">
                {{ getLabel('add_permission') }}
              </IvyButtonAction>
            </template>
          </PageEmptyState>
        </template>
      </IvyTable>

      <IvyShortDialog show-close align-center type="error" width="342px" />

      <div class="ivyforms-settings-permissions__pagination-container ivyforms-flex-shrink-0">
        <IvyPagination
          v-model:current-page="pagination.page"
          v-model:page-size="pagination.perPage"
          :total="total"
          :page-sizes="[10, 20, 50]"
          secondary
        />
      </div>
    </div>

    <IvyDialog
      v-model="modalOpen"
      :title="editingId ? getLabel('edit_permission') : getLabel('add_permission')"
      show-close
      align-center
      width="475px"
    >
      <div class="ivyforms-flex ivyforms-flex-direction-column ivyforms-gap-16">
        <div>
          <div class="regular-14 ivyforms-pb-8">{{ getLabel('target_type') }}</div>
          <IvyRadioGroup v-model="targetType" priority="secondary" direction="horizontal">
            <IvyRadio priority="secondary" label="role">{{ getLabel('target_role') }}</IvyRadio>
            <IvyRadio priority="secondary" label="user">{{ getLabel('target_user') }}</IvyRadio>
          </IvyRadioGroup>
        </div>
        <IvyDivider />

        <div v-if="targetType === 'role'">
          <div class="regular-14 ivyforms-pb-8">{{ getLabel('select_roles') }}</div>
          <IvySelectInput
            v-model="selectedRoles"
            multiple
            priority="secondary"
            :placeholder="getLabel('select_roles')"
            class="ivyforms-width-100"
          >
            <ElOption
              v-for="r in roleOptions"
              priority="secondary"
              :key="r.slug"
              :label="r.name"
              :value="r.slug"
            />
          </IvySelectInput>
        </div>

        <div v-else>
          <div class="regular-14 ivyforms-pb-8">{{ getLabel('select_users') }}</div>
          <IvySelectInput
            v-model="selectedUserIds"
            multiple
            remote
            :remote-method="searchUsers"
            :loading="usersLoading"
            :placeholder="getLabel('select_users')"
            class="ivyforms-width-100"
            @visible-change="onUserSelectVisibleChange"
          >
            <ElOption v-for="u in userOptions" :key="u.id" :label="u.email" :value="String(u.id)" />
          </IvySelectInput>
        </div>

        <div>
          <div class="regular-14 ivyforms-pb-8">{{ getLabel('permissions_column') }}</div>
          <div class="ivyforms-flex ivyforms-gap-24">
            <div
              class="ivyforms-flex ivyforms-flex-direction-column ivyforms-gap-8 ivyforms-flex-1"
            >
              <IvyCheckbox
                v-for="def in permissionColLeft"
                :key="def.key"
                v-model="permissionChecks[def.key]"
                priority="secondary"
                size="s"
                type="checkmark"
              >
                {{ def.label }}
              </IvyCheckbox>
            </div>
            <div
              class="ivyforms-flex ivyforms-flex-direction-column ivyforms-gap-8 ivyforms-flex-1"
            >
              <IvyCheckbox
                v-for="def in permissionColRight"
                :key="def.key"
                v-model="permissionChecks[def.key]"
                priority="secondary"
                size="s"
                type="checkmark"
              >
                {{ def.label }}
              </IvyCheckbox>
            </div>
          </div>
        </div>
        <IvyDivider />
        <IvyCheckbox v-model="limitToSpecificForms" priority="secondary" size="s" type="checkmark">
          {{ getLabel('limit_to_specific_forms') }}
        </IvyCheckbox>
        <div
          v-if="limitToSpecificForms"
          class="ivyforms-flex ivyforms-flex-direction-column ivyforms-gap-8"
        >
          <div class="regular-12 ivyforms-text-secondary">
            {{ getLabel('limit_to_specific_forms_hint') }}
          </div>
          <IvySelectInput
            v-model="selectedFormIds"
            multiple
            :placeholder="getLabel('select_forms')"
            class="ivyforms-width-100"
          >
            <ElOption v-for="f in formOptions" :key="f.id" :label="f.name" :value="String(f.id)" />
          </IvySelectInput>
        </div>
      </div>
      <template #footer>
        <IvyButtonAction priority="tertiary" type="fill" @click="modalOpen = false">
          {{ getLabel('cancel') }}
        </IvyButtonAction>
        <IvyButtonAction priority="primary" type="fill" :loading="saving" @click="saveModal">
          {{ getLabel('save') }}
        </IvyButtonAction>
      </template>
    </IvyDialog>
  </div>
</template>

<script setup lang="ts">
import { computed, h, onMounted, reactive, ref, watch } from 'vue'
import { ElOption } from 'element-plus'
import { useLabels } from '@/composables/useLabels'
import { useApiClient } from '@/composables/useApiClient'
import type { TableColumn } from '@/views/_components/table/IvyTableColumn.ts'
import { useTableSkeletonState } from '@/views/_components/table/composables/useTableSkeletonState.ts'
import { useTableWidth } from '@/views/_components/table/composables/useTableWidth.ts'
import { actionsColumn } from '@/views/_components/table/columns'
import IvyButtonAction from '@/views/_components/button/IvyButtonAction.vue'
import IvyCheckbox from '@/views/_components/checkbox/IvyCheckbox.vue'
import IvyDialog from '@/views/_components/dialog/IvyDialog.vue'
import IvyRadio from '@/views/_components/radio/IvyRadio.vue'
import IvyRadioGroup from '@/views/_components/radio/IvyRadioGroup.vue'
import IvySelectInput from '@/views/_components/input/IvySelectInput.vue'
import IvyMessage from '@/views/_components/message/ivyMessage.ts'
import IvyTooltip from '@/views/_components/tooltip/IvyTooltip.vue'
import PageEmptyState from '@/views/admin/parts/PageEmptyState.vue'
import IvyDivider from '@/views/_components/divider/IvyDivider.vue'
import IvyShortDialog from '@/views/_components/sub-dialog/IvyShortDialog.vue'
import { useActionEntityStore } from '@/stores/actionEntityStore'
import { debounce } from 'lodash'

interface PermissionDef {
  key: string
  label: string
}

interface RoleMeta {
  slug: string
  name: string
  assigned: boolean
}

interface FormMeta {
  id: number
  name: string
}

interface UserOption {
  id: number
  email: string
  label: string
}

interface RuleRow {
  id: string
  type: string
  role_slug?: string
  user_id?: number
  permissions: string[]
  form_ids: number[] | null
  created_at?: number
  updated_at?: number
  email_display?: string
  role_name?: string
  forms_display?: string
  permissions_display?: string
}

interface PermissionTableRow extends Record<string, unknown> {
  id: string
  email: string
  role: string
  forms: string
  permissions: string
  permissionKeysCsv: string
}

const { getLabel } = useLabels()
const { request } = useApiClient()
const actionEntityStore = useActionEntityStore()

const loading = ref(true)
const saving = ref(false)
const rules = ref<RuleRow[]>([])
const tableData = ref<PermissionTableRow[]>([])
const total = ref(0)
const metaPermissionDefs = ref<PermissionDef[]>([])
const metaRoles = ref<RoleMeta[]>([])
const formOptions = ref<FormMeta[]>([])

const modalOpen = ref(false)
const editingId = ref<string | null>(null)
const targetType = ref<'role' | 'user'>('role')
const selectedRoles = ref<string[]>([])
const selectedUserIds = ref<string[]>([])
const userOptions = ref<UserOption[]>([])
const usersLoading = ref(false)
const limitToSpecificForms = ref(false)
const selectedFormIds = ref<string[]>([])
const permissionChecks = reactive<Record<string, boolean>>({})
const pagination = reactive({
  page: 1,
  perPage: 10,
})

const COMPACT_ROW_HEIGHT = 52
const EXPANDED_ROW_HEIGHT = 70
const MAX_PILLS_FOR_COMPACT_ROW = 2

/** Layout width for Forms column (wider so long form names show in full when wrapped). */
const FORMS_COLUMN_WIDTH = 300

const permissionColLeft = computed(() => metaPermissionDefs.value.slice(0, 4))
const permissionColRight = computed(() => metaPermissionDefs.value.slice(4))

const tableRef = ref(null)
const { shouldShowSkeleton } = useTableSkeletonState({ loading })

const roleOptions = computed(() => metaRoles.value.filter((role) => role.slug !== 'administrator'))

function resetPermissionChecks() {
  for (const k of Object.keys(permissionChecks)) {
    delete permissionChecks[k]
  }
  for (const d of metaPermissionDefs.value) {
    permissionChecks[d.key] = false
  }
}

watch(metaPermissionDefs, resetPermissionChecks, { immediate: true })
watch(
  permissionChecks,
  () => {
    // Forms dependencies:
    // - Add/Edit forms OR Delete forms implies View forms list.
    if (permissionChecks.add_edit_forms || permissionChecks.delete_forms) {
      permissionChecks.view_forms_list = true
    }

    // Entries dependencies:
    // - Delete entries implies View entries.
    if (permissionChecks.delete_entries_admin) {
      permissionChecks.view_entries_admin = true
    }
  },
  { deep: true },
)
function mapRuleToTableRow(rule: RuleRow): PermissionTableRow {
  return {
    id: rule.id,
    email: rule.email_display ?? '',
    role: rule.type === 'user' ? getLabel('target_user') : rule.role_name || '—',
    forms: rule.forms_display ?? '',
    permissions: rule.permissions_display ?? '',
    permissionKeysCsv: (Array.isArray(rule.permissions) ? rule.permissions : []).join(','),
  }
}

function loadTableData() {
  const rows = rules.value.map(mapRuleToTableRow)
  total.value = rows.length

  const maxPage = Math.max(1, Math.ceil(rows.length / pagination.perPage) || 1)
  if (pagination.page > maxPage) {
    pagination.page = maxPage
  }

  const start = (pagination.page - 1) * pagination.perPage
  const end = start + pagination.perPage
  tableData.value = rows.slice(start, end)
}

watch([() => pagination.page, () => pagination.perPage, () => rules.value], () => {
  loadTableData()
})

async function loadMeta() {
  const { data, error, status } = await request('permissions/meta', { method: 'GET' })
  if (status !== 200 || error || !data?.data) {
    return
  }
  const inner = data.data as {
    permission_definitions?: PermissionDef[]
    roles?: RoleMeta[]
    forms?: FormMeta[]
  }
  metaPermissionDefs.value = inner.permission_definitions || []
  metaRoles.value = inner.roles || []
  formOptions.value = inner.forms || []
}

function pruneDeletedFormIdsFromRules(rows: RuleRow[]): RuleRow[] {
  const validIds = new Set(formOptions.value.map((form) => form.id))
  return rows.map((row) => {
    if (!Array.isArray(row.form_ids) || row.form_ids.length === 0) {
      return row
    }
    const formIds = row.form_ids.filter((id) => validIds.has(id))
    return formIds.length === row.form_ids.length ? row : { ...row, form_ids: formIds }
  })
}

async function loadRules() {
  loading.value = true
  try {
    const { data, error, status } = await request('permissions/access-rules', { method: 'GET' })
    if (status !== 200 || error || !data?.data) {
      IvyMessage({ message: getLabel('error_loading_permissions'), type: 'error' })
      return
    }
    const inner = data.data as { rules?: RuleRow[] }
    rules.value = pruneDeletedFormIdsFromRules(inner.rules || [])
    loadTableData()
  } finally {
    loading.value = false
  }
}

const MIN_USER_SEARCH_LENGTH = 2

function onUserSelectVisibleChange(visible: boolean) {
  if (visible && userOptions.value.length === 0) {
    void fetchUsers('')
  }
}

function mergeUserOptions(fetched: UserOption[]): UserOption[] {
  const byId = new Map<number, UserOption>()
  for (const option of userOptions.value) {
    if (selectedUserIds.value.includes(String(option.id))) {
      byId.set(option.id, option)
    }
  }
  for (const option of fetched) {
    byId.set(option.id, option)
  }
  return Array.from(byId.values())
}

async function fetchUsers(search: string) {
  const trimmed = search.trim()
  if (trimmed.length > 0 && trimmed.length < MIN_USER_SEARCH_LENGTH) {
    return
  }

  usersLoading.value = true
  const { data, error, status } = await request('permissions/users', {
    method: 'GET',
    params: { search: trimmed },
  })
  usersLoading.value = false
  if (status !== 200 || error || !data?.data) {
    return
  }
  const inner = data.data as { users?: UserOption[] }
  userOptions.value = mergeUserOptions(inner.users || [])
}

const searchUsers = debounce((query: string) => {
  void fetchUsers(query)
}, 300)

function openAddModal() {
  editingId.value = null
  targetType.value = 'role'
  selectedRoles.value = []
  selectedUserIds.value = []
  limitToSpecificForms.value = false
  selectedFormIds.value = []
  resetPermissionChecks()
  userOptions.value = []
  modalOpen.value = true
}

function openEditModal(row: RuleRow) {
  editingId.value = row.id
  targetType.value = row.type === 'user' ? 'user' : 'role'
  selectedRoles.value = row.type === 'role' && row.role_slug ? [row.role_slug] : []
  selectedUserIds.value = row.type === 'user' && row.user_id ? [String(row.user_id)] : []
  limitToSpecificForms.value = Array.isArray(row.form_ids) && row.form_ids.length > 0
  selectedFormIds.value = Array.isArray(row.form_ids) ? row.form_ids.map((id) => String(id)) : []
  resetPermissionChecks()
  for (const p of row.permissions || []) {
    if (p in permissionChecks) {
      permissionChecks[p] = true
    }
  }
  if (row.type === 'user' && row.user_id) {
    const email = row.email_display || ''
    userOptions.value = [
      {
        id: row.user_id,
        email,
        label: email,
      },
    ]
  } else {
    userOptions.value = []
  }
  modalOpen.value = true
  if (targetType.value === 'user') {
    void fetchUsers('')
  }
}

watch(targetType, (next) => {
  if (next === 'user' && modalOpen.value) {
    void fetchUsers('')
  }
})

function confirmDelete(row: RuleRow) {
  actionEntityStore.actionType = 'delete'
  actionEntityStore.showDialog({
    title: getLabel('confirm_delete_rule'),
    subtitle: getLabel('action_cannot_be_undone'),
    dialogType: 'error',
    buttons: {
      close: {
        type: 'tertiary',
        text: getLabel('cancel'),
      },
      confirm: {
        type: 'danger',
        text: getLabel('delete'),
        function: () => {
          rules.value = rules.value.filter((r) => r.id !== row.id)
          void persistRules()
        },
      },
    },
  })
}

function countDisplayPills(value: unknown): number {
  if (typeof value !== 'string') {
    return 0
  }

  return value
    .split(',')
    .map((item) => item.trim())
    .filter((item) => item.length > 0).length
}

const permissionsRowHeight = computed(() => {
  const needsExpandedRow = tableData.value.some((row) => {
    const formsPills = countDisplayPills(row.forms)
    const permissionsPills = countDisplayPills(row.permissions)

    return formsPills > MAX_PILLS_FOR_COMPACT_ROW || permissionsPills > MAX_PILLS_FOR_COMPACT_ROW
  })

  return needsExpandedRow ? EXPANDED_ROW_HEIGHT : COMPACT_ROW_HEIGHT
})

function splitDisplayValues(value: unknown): string[] {
  if (typeof value !== 'string') {
    return []
  }

  return value
    .split(',')
    .map((item) => item.trim())
    .filter((item) => item.length > 0)
}

function renderPillsCell(rawValue: unknown, maxVisible?: number, listClass = '') {
  const items = splitDisplayValues(rawValue)
  if (items.length === 0) {
    return h('span', { class: 'ivyforms-permissions-table__text-cell' }, '—')
  }

  const hasLimit = typeof maxVisible === 'number' && maxVisible > 0
  const visible = hasLimit ? items.slice(0, maxVisible) : items
  const hidden = hasLimit ? items.slice(maxVisible) : []
  const hiddenTooltip = hidden.join(', ')

  return h(
    'div',
    {
      class: ['ivyforms-permissions-table__pill-list', listClass].filter(Boolean).join(' '),
    },
    [
      ...visible.map((item) =>
        h('span', { class: 'ivyforms-permissions-table__pill', title: item }, item),
      ),
      ...(hidden.length > 0
        ? [
            h(
              IvyTooltip,
              {
                content: hiddenTooltip,
                placement: 'top',
              },
              () => [
                h(
                  'span',
                  {
                    class:
                      'ivyforms-permissions-table__pill ivyforms-permissions-table__pill--muted',
                    title: hiddenTooltip,
                  },
                  `+${hidden.length}`,
                ),
              ],
            ),
          ]
        : []),
    ],
  )
}

function renderPermissionsCell(rowData: Record<string, unknown>) {
  const csv = typeof rowData.permissionKeysCsv === 'string' ? rowData.permissionKeysCsv : ''
  const permissionKeys = csv.length > 0 ? csv.split(',').filter(Boolean) : []

  const allPermissionCount = metaPermissionDefs.value.length
  const hasAllPermissions = allPermissionCount > 0 && permissionKeys.length >= allPermissionCount

  if (hasAllPermissions) {
    return h('div', { class: 'ivyforms-permissions-table__pill-list' }, [
      h('span', { class: 'ivyforms-permissions-table__pill' }, 'All permissions'),
    ])
  }

  return renderPillsCell(rowData.permissions, 4)
}

const columns = ref<TableColumn[]>([
  {
    key: 'email',
    title: getLabel('permissions_email_column'),
    width: 200,
    minWidth: 180,
    visible: true,
    showInModal: false,
  },
  {
    key: 'role',
    title: getLabel('role_user_column'),
    width: 140,
    minWidth: 120,
    visible: true,
    showInModal: false,
  },
  {
    key: 'forms',
    title: getLabel('forms_column'),
    width: FORMS_COLUMN_WIDTH,
    minWidth: 220,
    visible: true,
    showInModal: false,
    class: 'ivyforms-permissions-forms-cell',
    cellRenderer: ({ rowData }) =>
      renderPillsCell(rowData.forms, 3, 'ivyforms-permissions-table__pill-list--forms'),
  },
  {
    key: 'permissions',
    title: getLabel('permissions_column'),
    width: 240,
    minWidth: 240,
    visible: true,
    showInModal: true,
    cellRenderer: ({ rowData }) => renderPermissionsCell(rowData as Record<string, unknown>),
  },
  actionsColumn<PermissionTableRow>({
    width: 104,
    minWidth: 104,
    title: getLabel('permissions_actions'),
    className: 'ivyforms-cell-actions',
    actions: [
      {
        icon: 'edit',
        tooltip: getLabel('edit'),
        onClick: (row) => {
          const rule = rules.value.find((r) => r.id === row.id)
          if (rule) openEditModal(rule)
        },
      },
      {
        icon: 'trash',
        tooltip: getLabel('delete'),
        priority: 'danger',
        onClick: (row) => {
          const rule = rules.value.find((r) => r.id === row.id)
          if (rule) confirmDelete(rule)
        },
      },
    ],
  }),
])

const { dynamicWidthColumns, minTableWidth: getMinTableWidth } = useTableWidth(tableRef, {
  columns,
  isDynamic: (col) => col.key === 'permissions',
})

function newId(): string {
  if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
    return crypto.randomUUID()
  }
  return `rule_${Date.now()}_${Math.random().toString(16).slice(2)}`
}

function stripRuleForApi(row: RuleRow): Record<string, unknown> {
  const base: Record<string, unknown> = {
    id: row.id,
    type: row.type,
    permissions: row.permissions,
    form_ids: row.form_ids,
    created_at: row.created_at ?? Math.floor(Date.now() / 1000),
    updated_at: Math.floor(Date.now() / 1000),
  }
  if (row.type === 'role') {
    base.role_slug = row.role_slug ?? ''
  } else if (row.type === 'user') {
    base.user_id = row.user_id ?? 0
  }
  return base
}

async function persistRules() {
  saving.value = true
  const payload = rules.value.map((r) => stripRuleForApi(r))
  const { error, status } = await request('permissions/access-rules', {
    method: 'PUT',
    data: { rules: payload },
    headers: { 'Content-Type': 'application/json' },
  })
  saving.value = false
  if (status !== 200 || error) {
    IvyMessage({ message: getLabel('error_saving_permissions'), type: 'error' })
    return
  }
  IvyMessage({ message: getLabel('permissions_saved'), type: 'success' })
  await loadMeta()
  await loadRules()
}

function selectedPermissionsList(): string[] {
  const selected = new Set(
    metaPermissionDefs.value.filter((d) => permissionChecks[d.key]).map((d) => d.key),
  )

  // Keep payload consistent even if checkboxes were toggled quickly.
  if (selected.has('add_edit_forms') || selected.has('delete_forms')) {
    selected.add('view_forms_list')
  }
  if (selected.has('delete_entries_admin')) {
    selected.add('view_entries_admin')
  }

  return metaPermissionDefs.value.map((d) => d.key).filter((key) => selected.has(key))
}

async function saveModal() {
  const perms = selectedPermissionsList()
  if (perms.length === 0) {
    IvyMessage({ message: getLabel('field_required'), type: 'error' })
    return
  }
  if (limitToSpecificForms.value && selectedFormIds.value.length === 0) {
    IvyMessage({ message: getLabel('select_forms'), type: 'error' })
    return
  }
  const formIds = limitToSpecificForms.value ? selectedFormIds.value.map((id) => Number(id)) : null
  const now = Math.floor(Date.now() / 1000)

  const existing = editingId.value ? rules.value.find((r) => r.id === editingId.value) : null
  const createdAt = existing?.created_at ?? now

  const newRows: RuleRow[] = []
  if (targetType.value === 'role') {
    if (selectedRoles.value.length === 0) {
      IvyMessage({ message: getLabel('select_roles'), type: 'error' })
      return
    }
    for (const slug of selectedRoles.value) {
      const existingForSlug = rules.value.find((r) => r.type === 'role' && r.role_slug === slug)
      newRows.push({
        id:
          existingForSlug?.id ??
          (editingId.value && selectedRoles.value.length === 1 ? editingId.value : newId()),
        type: 'role',
        role_slug: slug,
        permissions: perms,
        form_ids: formIds,
        created_at: existingForSlug?.created_at ?? createdAt,
        updated_at: now,
      })
    }
  } else {
    if (selectedUserIds.value.length === 0) {
      IvyMessage({ message: getLabel('select_users'), type: 'error' })
      return
    }
    if (editingId.value && selectedUserIds.value.length !== 1) {
      IvyMessage({ message: getLabel('select_users'), type: 'error' })
      return
    }
    for (const uidStr of selectedUserIds.value) {
      const uid = Number(uidStr)
      newRows.push({
        id: editingId.value && selectedUserIds.value.length === 1 ? editingId.value : newId(),
        type: 'user',
        user_id: uid,
        permissions: perms,
        form_ids: formIds,
        created_at: createdAt,
        updated_at: now,
      })
    }
  }

  let next = [...rules.value]
  if (targetType.value === 'role') {
    next = next.filter(
      (r) => !(r.type === 'role' && r.role_slug && selectedRoles.value.includes(r.role_slug)),
    )
  }
  if (editingId.value) {
    next = next.filter((r) => r.id !== editingId.value)
  }
  next.push(...newRows)
  rules.value = next
  modalOpen.value = false
  await persistRules()
}

onMounted(async () => {
  await loadMeta()
  await loadRules()
})
</script>

<style lang="scss">
.ivyforms-settings-permissions {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;

  &__table {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;

    // Multi-line pill cells need stretched rows (IvyTable defaults to center).
    .el-table-v2__row {
      align-items: stretch;
    }

    .el-table-v2__row-cell {
      div {
        display: flex;
        align-items: center;
        align-content: center;
        justify-content: center;
        flex-wrap: wrap;

        &.ivyforms-column-actions {
          display: none;
        }
      }
    }

    .el-table-v2__row {
      &.highlighted {
        background: var(--map-hover) !important;
      }

      &.hovered,
      &:hover {
        .el-table-v2__row-cell .ivyforms-column-actions {
          display: flex;
        }
      }

      .el-table-v2__row-cell .ivyforms-column-actions {
        justify-content: flex-end;
      }
    }

    .el-table-v2__header-cell[data-key='actions'] {
      justify-content: flex-end;
      padding-right: 14px;
    }

    .ivyforms-permissions-table__text-cell {
      display: block;
      width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      color: var(--map-base-text-0);
      line-height: 20px;
      text-align: start;
    }

    .ivyforms-permissions-table__pill-list {
      display: flex;
      align-items: center;
      gap: 6px;
      flex-wrap: wrap;
      width: 100%;
      max-width: 100%;
      overflow: visible;
      white-space: normal;
      min-height: 26px;
    }

    .ivyforms-permissions-forms-cell {
      overflow: visible !important;
      padding-top: 10px;
      padding-bottom: 10px;

      div {
        justify-content: flex-start;
      }
    }

    .ivyforms-permissions-table__pill-list--forms {
      align-items: flex-start;
      align-content: flex-start;
    }

    .ivyforms-permissions-table__pill {
      display: inline-flex;
      align-items: center;
      min-height: 26px;
      padding: 0 10px;
      border-radius: 8px;
      background: var(--map-base-dusk-o10);
      color: var(--map-base-text-0);
      font-size: 13px;
      font-weight: 500;
      line-height: 18px;
      max-width: 100%;
      white-space: normal;
      word-break: break-word;
      box-sizing: border-box;
    }

    .ivyforms-permissions-table__pill-list--forms .ivyforms-permissions-table__pill {
      max-width: 100%;
      width: fit-content;
      min-width: 0;
    }

    .ivyforms-permissions-table__pill--muted {
      background: var(--map-base-dusk-o1);
      color: var(--map-base-text-1);
      cursor: pointer;
    }

    .ivyforms-table-skeleton {
      .ivyforms-skeleton-row {
        height: 48px;
        margin-bottom: 8px;
        border-radius: 4px;
        animation: pulse 1.5s ease-in-out infinite;
      }
    }

    .el-table-v2__row {
      opacity: 0;
      animation: fadeIn 0.3s ease-in-out forwards;
      animation-delay: calc(var(--row-index, 0) * 0.05s);
    }
  }

  &__pagination-container {
    flex-shrink: 0;
    margin-top: 12px;
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes pulse {
  0% {
    opacity: 0.6;
  }
  50% {
    opacity: 0.3;
  }
  100% {
    opacity: 0.6;
  }
}
</style>
