<script setup lang="ts">
/**
 * RightDrawer — the global right-side drawer shell driven entirely by
 * `useRightDrawer()` / `useShellUiStore().drawer`. No open/title/flush props
 * (Decision A4): the caller passes chrome keys (`title`, `flush`) inside the
 * `props` object when calling `useRightDrawer().open('Name', { title, flush, ...bodyProps })`.
 *
 * PrimeVue <Drawer position="right"> provides the scrim, escape-key dismiss,
 * overlay-click close, and focus-trap. The writable computed `drawerVisible`
 * wires v-model:visible to useRightDrawer().isOpen / .close() without
 * mutating the store ref directly.
 *
 * Body component lifecycle:
 *   - `component.value` (string | null) is resolved via resolveDrawerComponent().
 *   - Unknown / unregistered names → null → graceful empty state (no crash).
 *     This is the expected state in the foundation scope; real drawer-body
 *     components register themselves via registerDrawerComponent() from their
 *     own feature zone (components-v2, pages-v2, etc.) at mount time.
 *   - Chrome keys `title` and `flush` are stripped from `bodyProps` and NOT
 *     forwarded to the body component — they are consumed at the shell level only.
 *
 * #actions slot:
 *   Parent layouts may inject header action buttons (e.g. an edit icon) via the
 *   named `#actions` slot. In the store-driven model this slot is typically unused,
 *   but it is kept for composed layouts that wrap <RightDrawer> and need to add
 *   persistent header controls without duplicating the chrome.
 *
 * CSS translation (crewli-starter main.css → Tailwind):
 *   .drawer              → handled by PrimeVue Drawer + :pt passthrough
 *   .drawer-head         → flex items-center gap-3 px-4 py-3 border-b ...
 *   .drawer-head .title  → flex-1 font-medium text-sm truncate
 *   .drawer-head .actions → ms-auto flex items-center gap-1
 *   .drawer-body (flush) → p-0 (edge-to-edge)
 *   .drawer-body (normal)→ p-4 overflow-y-auto
 *   .icon-btn            → rounded-md p-1 hover:bg-[var(--p-content-hover-background)]
 *                          transition-colors flex items-center justify-center
 */

import { computed } from 'vue'
import Drawer from 'primevue/drawer'
import Icon from '@/components/Icon.vue'
import { useRightDrawer } from '@/composables/useRightDrawer'

const { isOpen, component, props, close, resolveDrawerComponent } = useRightDrawer()

/**
 * Writable computed so PrimeVue Drawer can use v-model:visible without
 * directly mutating the Pinia store ref.
 */
const drawerVisible = computed<boolean>({
  get: () => isOpen.value,
  set: (v: boolean) => {
    if (!v)
      close()
  },
})

/**
 * Chrome keys read from drawerProps WITHOUT using `any`.
 * `typeof` narrowing instead of a cast keeps TypeScript strict.
 */
const title = computed(() =>
  typeof props.value.title === 'string' ? props.value.title : '',
)

const flush = computed(() => props.value.flush === true)

/**
 * Body props: `props.value` minus the chrome keys `title` and `flush`.
 * These are NOT forwarded to the body component — they are consumed by the
 * drawer shell. All other keys are passed through as-is.
 *
 * Key-filter approach avoids unused-variable linting errors from destructuring
 * (the project's varsIgnorePattern only allows purely-underscore names).
 */
const CHROME_KEYS = new Set(['title', 'flush'])

const bodyProps = computed(() =>
  Object.fromEntries(
    Object.entries(props.value).filter(([k]) => !CHROME_KEYS.has(k)),
  ),
)

/**
 * Resolved body component from the registry. Null when the component name is
 * unregistered (expected in foundation scope) — the template renders a graceful
 * empty state in that case.
 */
const Body = computed(() => resolveDrawerComponent(component.value))
</script>

<template>
  <Drawer
    v-model:visible="drawerVisible"
    position="right"
    :pt="{
      /*
       * Strip PrimeVue's default header so we render our own with the
       * close button, title, and #actions slot in a single controlled row.
       * content: remove default padding so body region controls its own spacing.
       */
      header: { class: 'hidden' },
      content: { class: 'flex flex-col p-0 overflow-hidden h-full' },
    }"
  >
    <!-- Drawer header: close button + title + optional #actions slot -->
    <div class="flex items-center gap-3 px-4 py-3 border-b border-[var(--p-content-border-color)] flex-shrink-0">
      <button
        type="button"
        aria-label="Close"
        class="rounded-md p-1 hover:bg-[var(--p-content-hover-background)] transition-colors flex items-center justify-center"
        @click="close"
      >
        <Icon
          name="tabler-x"
          :size="18"
        />
      </button>

      <div class="flex-1 font-medium text-sm truncate">
        {{ title }}
      </div>

      <!--
        #actions slot — parent layouts may inject header action buttons here.
        In the pure store-driven model this slot is typically empty.
      -->
      <div class="ms-auto flex items-center gap-1">
        <slot name="actions" />
      </div>
    </div>

    <!-- Drawer body: dynamic component or graceful empty state -->
    <div
      class="flex-1 overflow-y-auto"
      :class="flush ? 'p-0' : 'p-4'"
    >
      <!--
        Body is rendered only when a registered component is resolved.
        When Body.value is null (unknown / not-yet-registered name), we render
        a minimal empty state — this must NOT crash on null (resolveDrawerComponent
        guarantees null for unknown names).
      -->
      <component
        :is="Body"
        v-if="Body !== null"
        v-bind="bodyProps"
      />

      <!--
        Graceful empty state — shown when the component name is unregistered.
        Expected in foundation scope; body components self-register at mount.
      -->
      <p
        v-else
        class="text-sm text-[var(--p-text-muted-color)]"
      >
        Geen inhoud
      </p>
    </div>
  </Drawer>
</template>
