<script lang="ts">
/**
 * Public runtime exports for ShowPage. These live in a non-setup
 * `<script>` block because `<script setup>` disallows ES module
 * value exports — the InjectionKey symbol is a runtime value.
 */
import type { InjectionKey } from "vue"
import type { CardElement } from "@/definitions/elements"
import type { DisplayCard } from "@/definitions/cards"
import type {
  CreateConfig,
  DeleteConfig,
  UpdateConfig,
} from "@/definitions/shared/crud"

/**
 * Inject key used by elements / cards rendered under a ShowPage or
 * IndexPage to read the page-level eventHandlers map.
 */
export const PAGE_EVENT_HANDLERS_KEY = Symbol(
  "gx-modui:pageEventHandlers",
) as InjectionKey<Record<string, (...args: unknown[]) => void>>

export interface BreadCrumb {
  title: string
}

export interface TabConfig {
  title: string
  tabId: string
  cards: DisplayCard[]
  nextTab?: { id: string; text: string }
}

export interface ShowPageConfig {
  title: string
  breadCrumbs?: BreadCrumb[]
  section?: string
  tabsList?: TabConfig[]
  cards?: Record<string, DisplayCard>
  elements?: Record<string, CardElement>
  updateConfig?: UpdateConfig
  createConfig?: CreateConfig
  deleteConfig?: DeleteConfig
  eventHandlers?: Record<string, (...args: unknown[]) => void>
}

export interface ShowPageProps {
  config: ShowPageConfig
  /**
   * The data model rendered by the page. Each card / element receives
   * this as `item`.
   */
  item?: Record<string, unknown>
  /**
   * Override the form-data store ID. Defaults to `show-page-default`.
   */
  formId?: string
}
</script>

<script setup lang="ts">
/**
 * ShowPage — top-level templating consumer for detail / edit views.
 *
 * Renders:
 *   - page header (title, breadcrumbs, section)
 *   - either a tab list (when `config.tabsList` is provided) where each
 *     tab is a stack of CardRenderer instances, OR a flat stack of
 *     CardRenderer when only `config.cards` is provided
 *   - an optional save / delete action bar at the bottom when an
 *     `updateConfig` / `deleteConfig` is set
 *
 * Calls `provideFormId('show-page-default')` at the root so every
 * input element nested below binds to the same form-data store entry.
 *
 * Like IndexPage, exposes `config.eventHandlers` via provide/inject
 * under `PAGE_EVENT_HANDLERS_KEY` so deep descendants can `inject` and
 * emit named page-level events.
 */
import { computed, provide } from "vue"
import CardRenderer from "@/engine/CardRenderer.vue"
import { Button } from "@/components/ui/button"
import {
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
} from "@/components/ui/tabs"
import { provideFormId } from "@/engine/useFormData"
import { normalisePageConfig } from "@/adapters/detect"

const props = withDefaults(defineProps<ShowPageProps>(), {
  item: () => ({}),
  formId: "show-page-default",
})

/**
 * Normalised v2-shape config. Detects v1 JSON at the boundary and
 * runs it through `v1ToV2` so consumers can pass either shape.
 */
const normalisedConfig = computed<ShowPageConfig>(
  () =>
    normalisePageConfig(
      props.config as unknown as Parameters<typeof normalisePageConfig>[0],
    ) as unknown as ShowPageConfig,
)

const emit = defineEmits<{
  (e: "save"): void
  (e: "delete"): void
  (e: "tab-change", tabId: string): void
}>()

provideFormId(props.formId)
provide(
  PAGE_EVENT_HANDLERS_KEY,
  computed(() => normalisedConfig.value.eventHandlers ?? {}).value,
)

const breadCrumbs = computed<BreadCrumb[]>(
  () => normalisedConfig.value.breadCrumbs ?? [],
)

const tabsList = computed<TabConfig[]>(() => normalisedConfig.value.tabsList ?? [])

const flatCards = computed<DisplayCard[]>(() => {
  if (!normalisedConfig.value.cards) {
    return []
  }
  return Object.values(normalisedConfig.value.cards)
})

const showTabs = computed<boolean>(() => tabsList.value.length > 0)

const defaultTab = computed<string | undefined>(() => tabsList.value[0]?.tabId)

const showSaveButton = computed<boolean>(() => {
  const u = normalisedConfig.value.updateConfig
  if (!u) {
    return false
  }
  return u.showSaveButton !== false
})

const showDeleteButton = computed<boolean>(() => Boolean(normalisedConfig.value.deleteConfig))

const elementsMap = computed<Record<string, CardElement>>(
  () => normalisedConfig.value.elements ?? {},
)

const cardsMap = computed<Record<string, DisplayCard>>(
  () => normalisedConfig.value.cards ?? {},
)

function onSave(): void {
  const handler = normalisedConfig.value.eventHandlers?.save
  if (typeof handler === "function") {
    handler()
  }
  emit("save")
}

function onDelete(): void {
  const handler = normalisedConfig.value.eventHandlers?.delete
  if (typeof handler === "function") {
    handler()
  }
  emit("delete")
}

function onTabChange(value: string | number): void {
  const id = String(value)
  const handler = normalisedConfig.value.eventHandlers?.["tab-change"]
  if (typeof handler === "function") {
    handler(id)
  }
  emit("tab-change", id)
}
</script>

<template>
  <div class="gxp-show-page flex flex-col gap-4" data-testid="show-page">
    <header class="flex flex-col gap-2" data-testid="show-page-header">
      <nav
        v-if="breadCrumbs.length"
        aria-label="Breadcrumb"
        class="text-sm text-muted-foreground"
        data-testid="show-page-breadcrumbs"
      >
        <ol class="flex flex-wrap items-center gap-1">
          <li
            v-for="(crumb, index) in breadCrumbs"
            :key="`${crumb.title}-${index}`"
            class="flex items-center gap-1"
          >
            <span>{{ crumb.title }}</span>
            <span
              v-if="index < breadCrumbs.length - 1"
              aria-hidden="true"
              class="opacity-60"
            >
              /
            </span>
          </li>
        </ol>
      </nav>
      <div class="flex flex-col gap-0.5">
        <p
          v-if="normalisedConfig.section"
          class="text-xs font-medium uppercase tracking-wide text-muted-foreground"
          data-testid="show-page-section"
        >
          {{ normalisedConfig.section }}
        </p>
        <h1
          class="text-2xl font-semibold tracking-tight"
          data-testid="show-page-title"
        >
          {{ normalisedConfig.title }}
        </h1>
      </div>
    </header>

    <section class="gxp-show-page__body" data-testid="show-page-body">
      <Tabs
        v-if="showTabs && defaultTab"
        :default-value="defaultTab"
        class="flex flex-col gap-4"
        data-testid="show-page-tabs"
        @update:model-value="onTabChange"
      >
        <TabsList variant="underline">
          <TabsTrigger
            v-for="tab in tabsList"
            :key="tab.tabId"
            variant="underline"
            :value="tab.tabId"
            :data-testid="`show-page-tab-${tab.tabId}`"
          >
            {{ tab.title }}
          </TabsTrigger>
        </TabsList>
        <TabsContent
          v-for="tab in tabsList"
          :key="tab.tabId"
          :value="tab.tabId"
          class="flex flex-col gap-4"
          :data-testid="`show-page-tab-content-${tab.tabId}`"
        >
          <CardRenderer
            v-for="card in tab.cards"
            :key="card.id"
            :card="card"
            :item="item"
            :elements="elementsMap"
            :cards="cardsMap"
          />
        </TabsContent>
      </Tabs>

      <div
        v-else
        class="flex flex-col gap-4"
        data-testid="show-page-flat-cards"
      >
        <CardRenderer
          v-for="card in flatCards"
          :key="card.id"
          :card="card"
          :item="item"
          :elements="elementsMap"
          :cards="cardsMap"
        />
      </div>
    </section>

    <footer
      v-if="showSaveButton || showDeleteButton"
      class="flex items-center justify-end gap-2 border-t pt-4"
      data-testid="show-page-actions"
    >
      <Button
        v-if="showDeleteButton"
        variant="destructive"
        data-testid="show-page-delete-button"
        @click="onDelete"
      >
        Delete
      </Button>
      <Button
        v-if="showSaveButton"
        data-testid="show-page-save-button"
        @click="onSave"
      >
        Save
      </Button>
    </footer>
  </div>
</template>
