<script lang="ts">
  import { enhance } from "$app/forms";
  import ModalShell from "./ModalShell.svelte";
  import type {
    PermissionsCategorized,
    PermissionCategory,
    PermissionNode,
  } from "$lib/optima-api/modules/permissions";
  import {
    collectAllNodes,
    collectAllPermNodes,
    allSelectableStrings,
  } from "$lib/permissions";
  import type { Role } from "$lib/optima-api/modules/roles";

  export let isOpen = false;
  export let permissionNodes: PermissionsCategorized = {};
  export let roleToEdit: Role | null = null;
  export let onClose: () => void = () => {};
  export let onSuccess: () => void = () => {};

  $: isEditMode = roleToEdit !== null;

  $: if (isOpen && roleToEdit) {
    title = roleToEdit.title;
    moniker = roleToEdit.moniker;
    monikerEdited = true;
    selectedPermissions = new Set(roleToEdit.permissions);

    // Identify permissions not in the categorized list → show as custom
    const catalogued = new Set(
      Object.values(permissionNodes).flatMap((cat) =>
        collectAllNodes(cat as PermissionCategory),
      ),
    );
    customNodes = roleToEdit.permissions.filter((p) => !catalogued.has(p));

    // Auto-expand categories that contain selected permissions
    const sel = new Set(roleToEdit.permissions);
    const expanded = new Set<string>();
    for (const [key, cat] of Object.entries(permissionNodes) as [
      string,
      PermissionCategory,
    ][]) {
      const allNodes = collectAllNodes(cat);
      if (allNodes.some((n) => sel.has(n))) {
        expanded.add(key);
      }
      // Also auto-expand subcategories that contain selected permissions
      if (cat.subCategories) {
        for (const [subKey, subCat] of Object.entries(cat.subCategories) as [
          string,
          PermissionCategory,
        ][]) {
          if (collectAllNodes(subCat).some((n) => sel.has(n))) {
            expanded.add(`${key}/${subKey}`);
          }
        }
      }
    }
    expandedCategories = expanded;
  }

  let title = "";
  let moniker = "";
  let monikerEdited = false;
  let selectedPermissions = new Set<string>();
  let expandedCategories = new Set<string>();
  let isSubmitting = false;
  let submitError = "";
  let customPermNode = "";
  let customPermError = "";
  let customNodes: string[] = [];

  $: if (!monikerEdited) {
    moniker = title
      .toLowerCase()
      .replace(/\s+/g, "-")
      .replace(/[^a-z0-9-]/g, "");
  }

  $: permissionEntries = Object.entries(permissionNodes ?? {}) as [
    string,
    PermissionCategory,
  ][];

  $: isValid = title.trim().length > 0 && moniker.trim().length > 0;
  $: selectedCount = selectedPermissions.size;

  function togglePermission(node: string) {
    const next = new Set(selectedPermissions);
    next.has(node) ? next.delete(node) : next.add(node);
    selectedPermissions = next;
  }

  function toggleCategory(key: string) {
    const next = new Set(expandedCategories);
    next.has(key) ? next.delete(key) : next.add(key);
    expandedCategories = next;
  }

  function toggleAllInCategory(perms: PermissionNode[]) {
    const next = new Set(selectedPermissions);
    const all = allSelectableStrings(perms);
    const allSelected = all.every((s) => next.has(s));
    all.forEach((s) => (allSelected ? next.delete(s) : next.add(s)));
    selectedPermissions = next;
  }

  function toggleFieldLevelPerms(fieldPerms: string[]) {
    const next = new Set(selectedPermissions);
    const allSelected = fieldPerms.every((f) => next.has(f));
    fieldPerms.forEach((f) => (allSelected ? next.delete(f) : next.add(f)));
    selectedPermissions = next;
  }

  function addCustomPermission() {
    const node = customPermNode.trim();
    customPermError = "";
    if (!node) return;
    // Validate permission node format per PERMISSIONS.md:
    // - dot-separated tokens of lowercase alphanumeric + underscores
    // - special tokens: * (wildcard), ? (single-char wildcard)
    // - inclusive list [a,b,c] or exclusive list <a,b,c>
    // - standalone * for full access
    if (
      !/^(?:\*|(?:(?:[a-z0-9_]+|\*|\?|\[[a-z0-9_]+(?:,[a-z0-9_]+)*\]|<[a-z0-9_]+(?:,[a-z0-9_]+)*>)(?:\.(?:[a-z0-9_]+|\*|\?|\[[a-z0-9_]+(?:,[a-z0-9_]+)*\]|<[a-z0-9_]+(?:,[a-z0-9_]+)*>))*))$/.test(
        node,
      )
    ) {
      customPermError =
        "Use dot-separated tokens (e.g. resource.action). Supports: a-z, 0-9, underscores, * ? wildcards, [a,b] or <a,b> lists.";
      return;
    }
    if (selectedPermissions.has(node)) {
      customPermError = "This permission is already selected.";
      return;
    }
    // Add to custom list if not from the categorized set
    const catalogued = Object.values(permissionNodes).flatMap((cat) =>
      collectAllNodes(cat as PermissionCategory),
    );
    if (!catalogued.includes(node)) {
      customNodes = [...customNodes, node];
    }
    const next = new Set(selectedPermissions);
    next.add(node);
    selectedPermissions = next;
    customPermNode = "";
  }

  function reset() {
    title = "";
    moniker = "";
    monikerEdited = false;
    selectedPermissions = new Set();
    expandedCategories = new Set();
    isSubmitting = false;
    submitError = "";
    customPermNode = "";
    customPermError = "";
    customNodes = [];
  }

  function handleClose() {
    reset();
    onClose();
  }
</script>

<ModalShell
  {isOpen}
  title={isEditMode ? "Edit Role" : "Create Role"}
  onClose={handleClose}
>
  <svelte:fragment slot="icon">
    <svg
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      width="18"
      height="18"
    >
      <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
    </svg>
  </svelte:fragment>

  <form
    method="POST"
    action={isEditMode ? "?/updateRole" : "?/createRole"}
    use:enhance={({ formData }) => {
      // Append permissions at submit time instead of via reactive hidden inputs
      for (const node of selectedPermissions) {
        formData.append("permissions", node);
      }
      isSubmitting = true;
      submitError = "";
      return async ({ result, update }) => {
        isSubmitting = false;
        if (result.type === "success") {
          reset();
          onSuccess();
        } else if (result.type === "failure") {
          submitError = String(
            (result.data as Record<string, unknown>)?.message ??
              (isEditMode
                ? "Failed to update role."
                : "Failed to create role."),
          );
        } else if (result.type === "error") {
          submitError =
            result.error?.message ?? "An unexpected error occurred.";
        }
        await update({ reset: false });
      };
    }}
  >
    {#if isEditMode && roleToEdit}
      <input type="hidden" name="id" value={roleToEdit.id} />
    {/if}

    <div class="modal-body">
      {#if submitError}
        <div class="error-banner">
          <svg
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            width="14"
            height="14"
          >
            <circle cx="12" cy="12" r="10" />
            <line x1="12" y1="8" x2="12" y2="12" />
            <line x1="12" y1="16" x2="12.01" y2="16" />
          </svg>
          {submitError}
        </div>
      {/if}

      <div class="form-row">
        <div class="form-group">
          <label for="role-title">Title <span class="req">*</span></label>
          <input
            id="role-title"
            name="title"
            type="text"
            placeholder="e.g. Administrator"
            bind:value={title}
            required
          />
        </div>
        <div class="form-group">
          <label for="role-moniker">
            Moniker <span class="req">*</span>
            {#if !isEditMode}<span class="label-hint">Auto-generated</span>{/if}
          </label>
          <input
            id="role-moniker"
            name="moniker"
            type="text"
            placeholder="e.g. administrator"
            bind:value={moniker}
            on:input={() => (monikerEdited = true)}
            required
          />
        </div>
      </div>

      <div class="perm-section">
        <div class="perm-section-header">
          <span class="perm-label">Permissions</span>
          {#if selectedCount > 0}
            <span class="selected-badge">{selectedCount} selected</span>
          {/if}
        </div>

        <div class="perm-list">
          {#if Object.keys(permissionNodes).length === 0}
            <p class="perm-empty">No permission data available.</p>
          {:else}
            {#each permissionEntries as [catKey, category] (catKey)}
              {@const catPerms = category.permissions ?? []}
              {@const allCatPermNodes = collectAllPermNodes(category)}
              {@const allCatSelectables = allSelectableStrings(allCatPermNodes)}
              {@const allSel =
                allCatSelectables.length > 0 &&
                allCatSelectables.every((s) => selectedPermissions.has(s))}
              {@const someSel =
                !allSel &&
                allCatSelectables.some((s) => selectedPermissions.has(s))}
              {@const isExpanded = expandedCategories.has(catKey)}
              {@const hasSubCats =
                category.subCategories &&
                Object.keys(category.subCategories).length > 0}
              <div class="cat-group">
                <div class="cat-header">
                  <button
                    type="button"
                    class="cat-toggle"
                    on:click={() => toggleCategory(catKey)}
                    aria-expanded={isExpanded}
                  >
                    <svg
                      class="chevron"
                      class:rotated={isExpanded}
                      viewBox="0 0 24 24"
                      fill="none"
                      stroke="currentColor"
                      stroke-width="2.5"
                      width="11"
                      height="11"
                    >
                      <polyline points="9 18 15 12 9 6" />
                    </svg>
                    <span class="cat-name">{category.name}</span>
                    <span class="cat-count">{allCatSelectables.length}</span>
                  </button>
                  <button
                    type="button"
                    class="cat-all-btn"
                    on:click={() => toggleAllInCategory(allCatPermNodes)}
                    title={allSel
                      ? "Deselect all in category"
                      : "Select all in category"}
                  >
                    <div
                      class="cb"
                      class:cb-checked={allSel}
                      class:cb-indeterminate={someSel}
                    >
                      {#if allSel}
                        <svg
                          viewBox="0 0 10 10"
                          fill="none"
                          stroke="currentColor"
                          stroke-width="2"
                          width="8"
                          height="8"
                        >
                          <polyline points="1.5 5 4 8 8.5 2" />
                        </svg>
                      {:else if someSel}
                        <svg
                          viewBox="0 0 10 10"
                          fill="none"
                          stroke="currentColor"
                          stroke-width="2.5"
                          width="8"
                          height="8"
                        >
                          <line x1="1.5" y1="5" x2="8.5" y2="5" />
                        </svg>
                      {/if}
                    </div>
                    All
                  </button>
                </div>

                {#if isExpanded}
                  <!-- Direct permissions in this category -->
                  {#if catPerms.length > 0}
                    <div class="cat-perms">
                      {#each catPerms as perm (perm.node)}
                        {@const sel = selectedPermissions.has(perm.node)}
                        {@const hasFields =
                          perm.fieldLevelPermissions &&
                          perm.fieldLevelPermissions.length > 0}
                        {@const fieldsExpanded =
                          hasFields &&
                          expandedCategories.has(`field:${perm.node}`)}
                        <label class="perm-row" class:perm-sel={sel}>
                          <input
                            type="checkbox"
                            class="sr-only"
                            checked={sel}
                            on:change={() => togglePermission(perm.node)}
                          />
                          <div class="cb" class:cb-checked={sel}>
                            {#if sel}
                              <svg
                                viewBox="0 0 10 10"
                                fill="none"
                                stroke="currentColor"
                                stroke-width="2"
                                width="8"
                                height="8"
                              >
                                <polyline points="1.5 5 4 8 8.5 2" />
                              </svg>
                            {/if}
                          </div>
                          <div class="perm-text">
                            <code class="perm-node">{perm.node}</code>
                            <span class="perm-desc">{perm.description}</span>
                          </div>
                          {#if hasFields}
                            <button
                              type="button"
                              class="field-expand-btn"
                              on:click|preventDefault|stopPropagation={() =>
                                toggleCategory(`field:${perm.node}`)}
                              title={fieldsExpanded
                                ? "Hide field permissions"
                                : `Show ${perm.fieldLevelPermissions?.length} field permissions`}
                            >
                              <svg
                                class="chevron"
                                class:rotated={fieldsExpanded}
                                viewBox="0 0 24 24"
                                fill="none"
                                stroke="currentColor"
                                stroke-width="2.5"
                                width="10"
                                height="10"
                              >
                                <polyline points="9 18 15 12 9 6" />
                              </svg>
                              <span class="field-expand-count"
                                >{perm.fieldLevelPermissions?.length} fields</span
                              >
                            </button>
                          {/if}
                        </label>
                        {#if hasFields && fieldsExpanded}
                          {@const fieldAllSel =
                            perm.fieldLevelPermissions?.every((f) =>
                              selectedPermissions.has(f),
                            ) ?? false}
                          {@const fieldSomeSel =
                            !fieldAllSel &&
                            (perm.fieldLevelPermissions?.some((f) =>
                              selectedPermissions.has(f),
                            ) ??
                              false)}
                          <div class="field-perms-wrap">
                            <div class="field-perms-header">
                              <span class="field-perms-label"
                                >Field Permissions</span
                              >
                              <button
                                type="button"
                                class="cat-all-btn"
                                on:click={() =>
                                  toggleFieldLevelPerms(
                                    perm.fieldLevelPermissions ?? [],
                                  )}
                                title={fieldAllSel
                                  ? "Deselect all fields"
                                  : "Select all fields"}
                              >
                                <div
                                  class="cb"
                                  class:cb-checked={fieldAllSel}
                                  class:cb-indeterminate={fieldSomeSel}
                                >
                                  {#if fieldAllSel}
                                    <svg
                                      viewBox="0 0 10 10"
                                      fill="none"
                                      stroke="currentColor"
                                      stroke-width="2"
                                      width="8"
                                      height="8"
                                    >
                                      <polyline points="1.5 5 4 8 8.5 2" />
                                    </svg>
                                  {:else if fieldSomeSel}
                                    <svg
                                      viewBox="0 0 10 10"
                                      fill="none"
                                      stroke="currentColor"
                                      stroke-width="2.5"
                                      width="8"
                                      height="8"
                                    >
                                      <line x1="1.5" y1="5" x2="8.5" y2="5" />
                                    </svg>
                                  {/if}
                                </div>
                                All
                              </button>
                            </div>
                            {#each perm.fieldLevelPermissions ?? [] as fieldPerm (fieldPerm)}
                              {@const fSel = selectedPermissions.has(fieldPerm)}
                              <label
                                class="perm-row field-perm-row"
                                class:perm-sel={fSel}
                              >
                                <input
                                  type="checkbox"
                                  class="sr-only"
                                  checked={fSel}
                                  on:change={() => togglePermission(fieldPerm)}
                                />
                                <div class="cb" class:cb-checked={fSel}>
                                  {#if fSel}
                                    <svg
                                      viewBox="0 0 10 10"
                                      fill="none"
                                      stroke="currentColor"
                                      stroke-width="2"
                                      width="8"
                                      height="8"
                                    >
                                      <polyline points="1.5 5 4 8 8.5 2" />
                                    </svg>
                                  {/if}
                                </div>
                                <code class="perm-node field-perm-node"
                                  >{fieldPerm}</code
                                >
                              </label>
                            {/each}
                          </div>
                        {/if}
                      {/each}
                    </div>
                  {/if}

                  <!-- Subcategories (e.g. object-type field-level permissions) -->
                  {#if hasSubCats}
                    <div
                      class="sub-cat-list"
                      class:sub-cat-list-no-direct={catPerms.length === 0}
                    >
                      {#each Object.entries(category.subCategories ?? {}) as [subKey, subCat] (subKey)}
                        {@const subCategory = subCat}
                        {@const subPerms = subCategory.permissions ?? []}
                        {@const subAllNodes = collectAllPermNodes(subCategory)}
                        {@const subAllSelectables =
                          allSelectableStrings(subAllNodes)}
                        {@const subAllSel =
                          subAllSelectables.length > 0 &&
                          subAllSelectables.every((s) =>
                            selectedPermissions.has(s),
                          )}
                        {@const subSomeSel =
                          !subAllSel &&
                          subAllSelectables.some((s) =>
                            selectedPermissions.has(s),
                          )}
                        {@const subExpanded = expandedCategories.has(
                          `${catKey}/${subKey}`,
                        )}
                        <div class="sub-cat-group">
                          <div class="sub-cat-header">
                            <button
                              type="button"
                              class="sub-cat-toggle"
                              on:click={() =>
                                toggleCategory(`${catKey}/${subKey}`)}
                              aria-expanded={subExpanded}
                            >
                              <svg
                                class="chevron"
                                class:rotated={subExpanded}
                                viewBox="0 0 24 24"
                                fill="none"
                                stroke="currentColor"
                                stroke-width="2.5"
                                width="10"
                                height="10"
                              >
                                <polyline points="9 18 15 12 9 6" />
                              </svg>
                              <span class="sub-cat-name"
                                >{subCategory.name}</span
                              >
                              <span class="cat-count"
                                >{subAllSelectables.length}</span
                              >
                            </button>
                            <button
                              type="button"
                              class="cat-all-btn"
                              on:click={() => toggleAllInCategory(subAllNodes)}
                              title={subAllSel
                                ? "Deselect all in subcategory"
                                : "Select all in subcategory"}
                            >
                              <div
                                class="cb"
                                class:cb-checked={subAllSel}
                                class:cb-indeterminate={subSomeSel}
                              >
                                {#if subAllSel}
                                  <svg
                                    viewBox="0 0 10 10"
                                    fill="none"
                                    stroke="currentColor"
                                    stroke-width="2"
                                    width="8"
                                    height="8"
                                  >
                                    <polyline points="1.5 5 4 8 8.5 2" />
                                  </svg>
                                {:else if subSomeSel}
                                  <svg
                                    viewBox="0 0 10 10"
                                    fill="none"
                                    stroke="currentColor"
                                    stroke-width="2.5"
                                    width="8"
                                    height="8"
                                  >
                                    <line x1="1.5" y1="5" x2="8.5" y2="5" />
                                  </svg>
                                {/if}
                              </div>
                              All
                            </button>
                          </div>

                          {#if subExpanded}
                            <div class="cat-perms sub-cat-perms">
                              {#each subPerms as perm (perm.node)}
                                {@const sel = selectedPermissions.has(
                                  perm.node,
                                )}
                                {@const hasFields =
                                  perm.fieldLevelPermissions &&
                                  perm.fieldLevelPermissions.length > 0}
                                {@const fieldsExpanded =
                                  hasFields &&
                                  expandedCategories.has(`field:${perm.node}`)}
                                <label class="perm-row" class:perm-sel={sel}>
                                  <input
                                    type="checkbox"
                                    class="sr-only"
                                    checked={sel}
                                    on:change={() =>
                                      togglePermission(perm.node)}
                                  />
                                  <div class="cb" class:cb-checked={sel}>
                                    {#if sel}
                                      <svg
                                        viewBox="0 0 10 10"
                                        fill="none"
                                        stroke="currentColor"
                                        stroke-width="2"
                                        width="8"
                                        height="8"
                                      >
                                        <polyline points="1.5 5 4 8 8.5 2" />
                                      </svg>
                                    {/if}
                                  </div>
                                  <div class="perm-text">
                                    <code class="perm-node">{perm.node}</code>
                                    <span class="perm-desc"
                                      >{perm.description}</span
                                    >
                                  </div>
                                  {#if hasFields}
                                    <button
                                      type="button"
                                      class="field-expand-btn"
                                      on:click|preventDefault|stopPropagation={() =>
                                        toggleCategory(`field:${perm.node}`)}
                                      title={fieldsExpanded
                                        ? "Hide field permissions"
                                        : `Show ${perm.fieldLevelPermissions?.length} field permissions`}
                                    >
                                      <svg
                                        class="chevron"
                                        class:rotated={fieldsExpanded}
                                        viewBox="0 0 24 24"
                                        fill="none"
                                        stroke="currentColor"
                                        stroke-width="2.5"
                                        width="10"
                                        height="10"
                                      >
                                        <polyline points="9 18 15 12 9 6" />
                                      </svg>
                                      <span class="field-expand-count"
                                        >{perm.fieldLevelPermissions?.length} fields</span
                                      >
                                    </button>
                                  {/if}
                                </label>
                                {#if hasFields && fieldsExpanded}
                                  {@const fieldAllSel =
                                    perm.fieldLevelPermissions?.every((f) =>
                                      selectedPermissions.has(f),
                                    ) ?? false}
                                  {@const fieldSomeSel =
                                    !fieldAllSel &&
                                    (perm.fieldLevelPermissions?.some((f) =>
                                      selectedPermissions.has(f),
                                    ) ??
                                      false)}
                                  <div
                                    class="field-perms-wrap sub-field-perms-wrap"
                                  >
                                    <div class="field-perms-header">
                                      <span class="field-perms-label"
                                        >Field Permissions</span
                                      >
                                      <button
                                        type="button"
                                        class="cat-all-btn"
                                        on:click={() =>
                                          toggleFieldLevelPerms(
                                            perm.fieldLevelPermissions ?? [],
                                          )}
                                      >
                                        <div
                                          class="cb"
                                          class:cb-checked={fieldAllSel}
                                          class:cb-indeterminate={fieldSomeSel}
                                        >
                                          {#if fieldAllSel}
                                            <svg
                                              viewBox="0 0 10 10"
                                              fill="none"
                                              stroke="currentColor"
                                              stroke-width="2"
                                              width="8"
                                              height="8"
                                            >
                                              <polyline
                                                points="1.5 5 4 8 8.5 2"
                                              />
                                            </svg>
                                          {:else if fieldSomeSel}
                                            <svg
                                              viewBox="0 0 10 10"
                                              fill="none"
                                              stroke="currentColor"
                                              stroke-width="2.5"
                                              width="8"
                                              height="8"
                                            >
                                              <line
                                                x1="1.5"
                                                y1="5"
                                                x2="8.5"
                                                y2="5"
                                              />
                                            </svg>
                                          {/if}
                                        </div>
                                        All
                                      </button>
                                    </div>
                                    {#each perm.fieldLevelPermissions ?? [] as fieldPerm (fieldPerm)}
                                      {@const fSel =
                                        selectedPermissions.has(fieldPerm)}
                                      <label
                                        class="perm-row field-perm-row"
                                        class:perm-sel={fSel}
                                      >
                                        <input
                                          type="checkbox"
                                          class="sr-only"
                                          checked={fSel}
                                          on:change={() =>
                                            togglePermission(fieldPerm)}
                                        />
                                        <div class="cb" class:cb-checked={fSel}>
                                          {#if fSel}
                                            <svg
                                              viewBox="0 0 10 10"
                                              fill="none"
                                              stroke="currentColor"
                                              stroke-width="2"
                                              width="8"
                                              height="8"
                                            >
                                              <polyline
                                                points="1.5 5 4 8 8.5 2"
                                              />
                                            </svg>
                                          {/if}
                                        </div>
                                        <code class="perm-node field-perm-node"
                                          >{fieldPerm}</code
                                        >
                                      </label>
                                    {/each}
                                  </div>
                                {/if}
                              {/each}
                            </div>
                          {/if}
                        </div>
                      {/each}
                    </div>
                  {/if}
                {/if}
              </div>
            {/each}
          {/if}

          <!-- Custom permission nodes -->
          {#if customNodes.length > 0}
            <div class="cat-group">
              <div class="cat-header">
                <div class="cat-toggle custom-cat-label">
                  <svg
                    viewBox="0 0 24 24"
                    fill="none"
                    stroke="currentColor"
                    stroke-width="2"
                    width="11"
                    height="11"
                  >
                    <line x1="12" y1="5" x2="12" y2="19" />
                    <line x1="5" y1="12" x2="19" y2="12" />
                  </svg>
                  <span class="cat-name">Custom</span>
                  <span class="cat-count">{customNodes.length}</span>
                </div>
              </div>
              <div class="cat-perms">
                {#each customNodes as node (node)}
                  {@const sel = selectedPermissions.has(node)}
                  <label class="perm-row" class:perm-sel={sel}>
                    <input
                      type="checkbox"
                      class="sr-only"
                      checked={sel}
                      on:change={() => togglePermission(node)}
                    />
                    <div class="cb" class:cb-checked={sel}>
                      {#if sel}
                        <svg
                          viewBox="0 0 10 10"
                          fill="none"
                          stroke="currentColor"
                          stroke-width="2"
                          width="8"
                          height="8"
                        >
                          <polyline points="1.5 5 4 8 8.5 2" />
                        </svg>
                      {/if}
                    </div>
                    <div class="perm-text">
                      <code class="perm-node">{node}</code>
                      <span class="perm-desc">Custom permission</span>
                    </div>
                  </label>
                {/each}
              </div>
            </div>
          {/if}
        </div>

        <!-- Custom permission input -->
        <div class="custom-perm-wrap">
          <input
            type="text"
            class="custom-perm-input"
            placeholder="Add custom node… (e.g. my.custom.node)"
            bind:value={customPermNode}
            on:keydown={(e) =>
              e.key === "Enter" && (e.preventDefault(), addCustomPermission())}
          />
          <button
            type="button"
            class="custom-perm-btn"
            on:click={addCustomPermission}
            disabled={!customPermNode.trim()}
          >
            <svg
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              width="13"
              height="13"
            >
              <line x1="12" y1="5" x2="12" y2="19" />
              <line x1="5" y1="12" x2="19" y2="12" />
            </svg>
            Add
          </button>
        </div>
        {#if customPermError}
          <p class="custom-perm-error">{customPermError}</p>
        {/if}
      </div>
    </div>

    <div class="modal-footer">
      <button
        type="button"
        class="btn-cancel"
        on:click={handleClose}
        disabled={isSubmitting}
      >
        Cancel
      </button>
      <button
        type="submit"
        class="btn-create"
        disabled={!isValid || isSubmitting}
      >
        {isSubmitting
          ? isEditMode
            ? "Saving…"
            : "Creating…"
          : isEditMode
            ? "Save Changes"
            : "Create Role"}
      </button>
    </div>
  </form>
</ModalShell>

<style>
  /* ── Form wraps body + footer so the submit btn works ── */
  form {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
  }

  /* ── Body ── */
  .modal-body {
    padding: 20px;
    overflow-y: auto;
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 16px;
    min-height: 0;
  }

  /* ── Error banner ── */
  .error-banner {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    background: rgba(220, 38, 38, 0.08);
    border: 1px solid rgba(220, 38, 38, 0.2);
    border-radius: 7px;
    font-size: 13px;
    color: #dc2626;
    flex-shrink: 0;
  }

  /* ── Form fields ── */
  .form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
  }

  .form-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }

  .form-group label {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
  }

  .req {
    color: var(--accent-color, #0066cc);
  }

  .label-hint {
    font-size: 11px;
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
    color: var(--text-muted);
  }

  .form-group input {
    padding: 8px 10px;
    background: var(--bg-inset);
    border: 1px solid var(--border-subtle);
    border-radius: 7px;
    font-size: 13px;
    color: var(--text-primary);
    outline: none;
    width: 100%;
    box-sizing: border-box;
    transition:
      border-color 0.15s,
      box-shadow 0.15s;
  }

  .form-group input::placeholder {
    color: var(--text-muted);
  }

  .form-group input:focus {
    border-color: var(--accent-color, #0066cc);
    box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.12);
  }

  /* ── Permissions section ── */
  .perm-section {
    display: flex;
    flex-direction: column;
    gap: 8px;
  }

  .perm-section-header {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .perm-label {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
  }

  .selected-badge {
    font-size: 11px;
    font-weight: 500;
    padding: 2px 8px;
    border-radius: 10px;
    background: var(--accent-color, #0066cc);
    color: #fff;
  }

  /* ── Custom permission input ── */
  .custom-perm-wrap {
    display: flex;
    gap: 6px;
    margin-top: 8px;
  }

  .custom-perm-input {
    flex: 1;
    padding: 7px 10px;
    border: 1px solid var(--border-subtle);
    border-radius: 7px;
    background: var(--bg-inset);
    font-family: ui-monospace, "SF Mono", "Cascadia Code", Menlo, monospace;
    font-size: 12px;
    color: var(--text-primary);
    outline: none;
    box-sizing: border-box;
    transition:
      border-color 0.15s,
      box-shadow 0.15s;
  }

  .custom-perm-input::placeholder {
    color: var(--text-muted);
    font-family: inherit;
  }

  .custom-perm-input:focus {
    border-color: var(--accent-color, #0066cc);
    box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.12);
  }

  .custom-perm-btn {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 7px 14px;
    background: var(--accent-color, #0066cc);
    color: #fff;
    border: none;
    border-radius: 7px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    transition: filter 0.15s;
  }

  .custom-perm-btn:hover:not(:disabled) {
    filter: brightness(1.1);
  }

  .custom-perm-btn:disabled {
    opacity: 0.45;
    cursor: not-allowed;
  }

  .custom-perm-error {
    margin: 4px 0 0;
    font-size: 11.5px;
    color: #dc2626;
  }

  .custom-cat-label {
    cursor: default;
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 7px 10px;
  }

  .custom-cat-label svg {
    color: var(--text-muted);
    flex-shrink: 0;
  }

  /* ── Permission list ── */
  .perm-list {
    border: 1px solid var(--border-subtle);
    border-radius: 8px;
    overflow-y: auto;
    max-height: 240px;
    background: var(--bg-inset);
  }

  .perm-empty {
    margin: 0;
    padding: 20px;
    font-size: 13px;
    color: var(--text-muted);
    text-align: center;
  }

  /* ── Category group ── */
  .cat-group {
    border-bottom: 1px solid var(--border-subtle);
  }

  .cat-group:last-child {
    border-bottom: none;
  }

  .cat-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-right: 10px;
  }

  .cat-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1;
    padding: 7px 10px;
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
    transition: background 0.1s;
  }

  .cat-toggle:hover {
    background: var(--card-hover-bg);
  }

  .chevron {
    color: var(--text-muted);
    flex-shrink: 0;
    transition: transform 0.15s ease;
  }

  .chevron.rotated {
    transform: rotate(90deg);
  }

  .cat-name {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-primary);
  }

  .cat-count {
    font-size: 11px;
    color: var(--text-muted);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    padding: 1px 6px;
    border-radius: 8px;
  }

  .cat-all-btn {
    display: flex;
    align-items: center;
    gap: 5px;
    padding: 3px 8px;
    background: none;
    border: 1px solid var(--border-subtle);
    border-radius: 5px;
    cursor: pointer;
    font-size: 11px;
    font-weight: 500;
    color: var(--text-secondary);
    flex-shrink: 0;
    transition:
      background 0.1s,
      border-color 0.1s;
  }

  .cat-all-btn:hover {
    background: var(--card-hover-bg);
    border-color: var(--border-default);
  }

  /* ── Custom checkbox ── */
  .cb {
    width: 13px;
    height: 13px;
    border: 1.5px solid var(--border-default);
    border-radius: 3px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    background: var(--bg-surface);
    color: #fff;
    transition:
      background 0.1s,
      border-color 0.1s;
  }

  .cb.cb-checked {
    background: var(--accent-color, #0066cc);
    border-color: var(--accent-color, #0066cc);
  }

  .cb.cb-indeterminate {
    background: var(--bg-surface);
    border-color: var(--accent-color, #0066cc);
    color: var(--accent-color, #0066cc);
  }

  /* ── Permission rows ── */
  .cat-perms {
    padding: 2px 0 4px;
    border-top: 1px solid var(--border-subtle);
  }

  /* ── Subcategory groups (e.g. Object Type field-level permissions) ── */
  .sub-cat-list {
    border-top: 1px solid var(--border-subtle);
    padding: 0;
  }

  .sub-cat-list-no-direct {
    border-top: 1px solid var(--border-subtle);
  }

  .sub-cat-group {
    border-bottom: 1px solid var(--border-subtle);
  }

  .sub-cat-group:last-child {
    border-bottom: none;
  }

  .sub-cat-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-right: 10px;
  }

  .sub-cat-toggle {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1;
    padding: 6px 10px 6px 22px;
    background: none;
    border: none;
    cursor: pointer;
    text-align: left;
    transition: background 0.1s;
  }

  .sub-cat-toggle:hover {
    background: var(--card-hover-bg);
  }

  .sub-cat-name {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-secondary);
  }

  .sub-cat-perms {
    padding-left: 12px;
  }

  .perm-row {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    padding: 6px 12px 6px 26px;
    cursor: pointer;
    transition: background 0.1s;
  }

  .perm-row:hover {
    background: var(--card-hover-bg);
  }

  .perm-row.perm-sel {
    background: rgba(0, 102, 204, 0.05);
  }

  .perm-row .cb {
    margin-top: 1px;
  }

  .perm-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
  }

  .perm-node {
    font-family: ui-monospace, "SF Mono", "Cascadia Code", Menlo, monospace;
    font-size: 11.5px;
    font-weight: 500;
    color: var(--text-primary);
    background: none;
    padding: 0;
  }

  .perm-desc {
    font-size: 11px;
    color: var(--text-muted);
    line-height: 1.4;
  }

  /* ── Field-level permissions ── */
  .field-expand-btn {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    margin-left: auto;
    padding: 2px 6px;
    background: none;
    border: 1px solid var(--border-subtle);
    border-radius: 4px;
    cursor: pointer;
    flex-shrink: 0;
    transition:
      background 0.1s,
      border-color 0.1s;
  }

  .field-expand-btn:hover {
    background: var(--card-hover-bg);
    border-color: var(--border-default);
  }

  .field-expand-count {
    font-size: 10px;
    font-weight: 500;
    color: var(--text-muted);
    white-space: nowrap;
  }

  .field-perms-wrap {
    margin: 0 0 4px;
    padding: 4px 0 4px 40px;
    border-left: 2px solid var(--border-subtle);
    margin-left: 32px;
  }

  .sub-field-perms-wrap {
    margin-left: 32px;
  }

  .field-perms-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 2px 8px 4px 4px;
  }

  .field-perms-label {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--text-muted);
  }

  .field-perm-row {
    padding: 3px 8px 3px 4px;
  }

  .field-perm-node {
    font-size: 10.5px;
    color: var(--text-secondary);
  }

  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
  }

  /* ── Footer ── */
  .modal-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    padding: 14px 20px;
    border-top: 1px solid var(--border-subtle);
    flex-shrink: 0;
  }

  .btn-cancel,
  .btn-create {
    padding: 7px 16px;
    border-radius: 7px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition:
      background 0.15s,
      border-color 0.15s;
  }

  .btn-cancel {
    background: var(--bg-inset);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
  }

  .btn-cancel:hover:not(:disabled) {
    background: var(--card-hover-bg);
    border-color: var(--border-default);
    color: var(--text-primary);
  }

  .btn-create {
    background: var(--accent-color, #0066cc);
    border: 1px solid transparent;
    color: #fff;
  }

  .btn-create:hover:not(:disabled) {
    filter: brightness(1.1);
  }

  .btn-cancel:disabled,
  .btn-create:disabled {
    opacity: 0.45;
    cursor: not-allowed;
  }
</style>
