<script setup lang="ts">
/**
 * Props-driven `grid-view` composition — a responsive CSS-grid layout
 * driven by `gridLayout`. Each `GridItem.i` is matched to one of the
 * resolved `cards` (or `gridElements`) by `id` and rendered into a CSS
 * grid cell using the item's `x`, `y`, `w`, `h` coordinates.
 *
 * Self-contained — directly mountable in any Vue app:
 *
 *   <GridViewCard
 *     :grid-layout="layout"
 *     :cards="[chartCard, feedCard]"
 *     :item="dataModel"
 *   />
 *
 * The full vue-grid-layout drag/resize behaviour from v2 is not yet
 * implemented; this composition focuses on the static visual layout.
 */
import { computed } from "vue"
import type { CardElement } from "@/definitions/elements"
import type { DisplayCard } from "@/definitions/cards"
import type {
  GridBreakpoint,
  GridItem,
  GridLayout,
} from "@/definitions/cards/GridViewCard"
import CardRenderer from "@/engine/CardRenderer.vue"
import ElementRenderer from "@/engine/ElementRenderer.vue"

export interface GridViewCardProps {
  title?: string
  subtitle?: string
  showCard?: boolean
  /** Per-breakpoint item layout map. */
  gridLayout?: GridLayout
  /** Pixel height of a single grid row. */
  rowHeight?: number
  /** Horizontal and vertical gap between items, in pixels. */
  margin?: [number, number]
  /** Padding around the entire grid container, in pixels. */
  containerPadding?: [number, number]
  /** Column count per breakpoint. */
  cols?: Partial<Record<GridBreakpoint, number>>
  /** Resolved nested cards (NOT IDs). */
  cards?: DisplayCard[]
  /** Resolved nested elements (NOT IDs) for grid items that target elements. */
  gridElements?: CardElement[]
  /** Page-level data model passed through to child renderers. */
  item?: Record<string, unknown>
}

const props = withDefaults(defineProps<GridViewCardProps>(), {
  showCard: true,
  rowHeight: 150,
  margin: () => [10, 10] as [number, number],
  containerPadding: () => [10, 10] as [number, number],
  cards: () => [],
  gridElements: () => [],
  item: () => ({}),
})

const BREAKPOINT_PRIORITY: GridBreakpoint[] = [
  "xxl",
  "xl",
  "lg",
  "md",
  "sm",
  "xs",
  "xxs",
]

const items = computed<GridItem[]>(() => {
  const layout = props.gridLayout ?? {}
  for (const bp of BREAKPOINT_PRIORITY) {
    const entry = layout[bp]
    if (entry && entry.length > 0) {
      return entry
    }
  }
  return []
})

const columnCount = computed<number>(() => {
  const cols = props.cols ?? {}
  for (const bp of BREAKPOINT_PRIORITY) {
    if (typeof cols[bp] === "number") {
      return cols[bp] as number
    }
  }
  return 12
})

const gridStyle = computed(() => ({
  display: "grid",
  gridTemplateColumns: `repeat(${columnCount.value}, minmax(0, 1fr))`,
  gridAutoRows: `${props.rowHeight}px`,
  columnGap: `${props.margin[0]}px`,
  rowGap: `${props.margin[1]}px`,
  padding: `${props.containerPadding[1]}px ${props.containerPadding[0]}px`,
}))

function cellStyle(slot: GridItem): Record<string, string> {
  return {
    gridColumn: `${slot.x + 1} / span ${slot.w}`,
    gridRow: `${slot.y + 1} / span ${slot.h}`,
  }
}

const cardById = computed<Record<string, DisplayCard>>(() => {
  const out: Record<string, DisplayCard> = {}
  for (const c of props.cards) {
    out[c.id] = c
  }
  return out
})

const elementById = computed<Record<string, CardElement>>(() => {
  const out: Record<string, CardElement> = {}
  for (const e of props.gridElements) {
    out[e.id] = e
  }
  return out
})

function resolveCard(id: string): DisplayCard | undefined {
  return cardById.value[id]
}

function resolveElement(id: string): CardElement | undefined {
  return elementById.value[id]
}

const showChrome = computed<boolean>(() => props.showCard !== false)
</script>

<template>
  <section
    :class="[
      'gxp-grid-view',
      showChrome
        ? 'rounded-lg border bg-card text-card-foreground shadow-sm p-4'
        : '',
    ]"
  >
    <header
      v-if="title || subtitle"
      class="mb-3 flex flex-col gap-0.5"
    >
      <h3 v-if="title" class="text-base font-semibold">{{ title }}</h3>
      <p v-if="subtitle" class="text-sm text-muted-foreground">
        {{ subtitle }}
      </p>
    </header>
    <div
      v-if="items.length > 0"
      :style="gridStyle"
      data-testid="grid-view-container"
    >
      <div
        v-for="slot in items"
        :key="slot.i"
        :style="cellStyle(slot)"
        :data-grid-item-id="slot.i"
        :class="[
          'gxp-grid-view__cell',
          slot.options?.displayBorder ? 'border rounded-md p-2' : '',
        ]"
      >
        <CardRenderer
          v-if="resolveCard(slot.i)"
          :card="resolveCard(slot.i)!"
          :item="item"
        />
        <ElementRenderer
          v-else-if="resolveElement(slot.i)"
          :element="resolveElement(slot.i)!"
          :item="item"
        />
      </div>
    </div>
    <p
      v-else
      class="text-sm text-muted-foreground italic"
      data-testid="grid-view-empty"
    >
      No grid items to display.
    </p>
  </section>
</template>
