<script setup lang="ts">
/**
 * PresetBuilder — wraps a FormBuilder schema with a named, described
 * preset envelope.
 *
 * Layout:
 *   - Canvas header: preset name + description inputs.
 *   - Canvas body:   embedded FormBuilder bound to the inner schema.
 *
 * State for the name + description lives in `presetBuilderStore`. The
 * inner schema is owned by `formBuilderStore` (keyed off a derived
 * `innerBuilderId`) so all existing FormBuilder behaviours work
 * unchanged.
 *
 * Two-way binds the full `PresetBuilderSchema` via `defineModel`.
 */
import { computed, onMounted, ref, watch } from "vue"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Badge } from "@/components/ui/badge"
import {
  FieldFrame,
  type FieldFrameProps,
} from "@/components/ui/field-frame"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
import FormBuilder from "./FormBuilder.vue"
import {
  usePresetBuilder,
  type PresetBuilderSchema,
} from "@/stores/presetBuilderStore"
import {
  useFormBuilder,
  type BuilderSchema,
} from "@/stores/formBuilderStore"
import { cn } from "@/lib/utils"

export interface PresetBuilderProps
  extends Pick<FieldFrameProps, "label" | "hideLabel" | "helpText" | "errors"> {
  /** Initial / current schema. Two-way bindable via v-model. */
  modelValue?: PresetBuilderSchema
  /**
   * Identifier used to key the presetBuilderStore. Defaults to a
   * stable auto-generated id so multiple PresetBuilders on the same
   * page don't collide.
   */
  builderId?: string
  name?: string | null
  item?: Record<string, unknown>
  classList?: string[] | string
}

const props = withDefaults(defineProps<PresetBuilderProps>(), {
  errors: () => [],
  item: () => ({}),
})

const model = defineModel<PresetBuilderSchema | undefined>("modelValue")

let nextAutoId = 0
function makeAutoId(): string {
  nextAutoId += 1
  return `preset-builder-${Date.now()}-${nextAutoId}`
}
const fallbackId = ref<string>(makeAutoId())
const builderId = computed<string>(() => props.builderId ?? fallbackId.value)

const builder = usePresetBuilder(builderId.value)
const innerBuilderId = computed<string>(() => builder.state.value.innerBuilderId)
const inner = useFormBuilder(innerBuilderId.value)

function isPresetSchema(value: unknown): value is PresetBuilderSchema {
  if (typeof value !== "object" || value === null) {
    return false
  }
  const v = value as Record<string, unknown>
  return (
    typeof v.presetName === "string" &&
    typeof v.schema === "object" &&
    v.schema !== null
  )
}

/** Seed both stores from the initial modelValue. */
onMounted(() => {
  const seed = model.value
  if (isPresetSchema(seed)) {
    builder.loadMeta({
      presetName: seed.presetName,
      presetDescription: seed.presetDescription,
    })
    inner.loadSchema(seed.schema)
  }
})

const innerSchemaRef = computed<BuilderSchema>(() => inner.schema.value)
const stateRef = computed(() => builder.state.value)

/** Push schema changes back via v-model. */
watch(
  [stateRef, innerSchemaRef],
  ([state, schema]) => {
    model.value = {
      presetName: state.presetName,
      presetDescription: state.presetDescription,
      schema,
    }
  },
  { deep: true },
)

function onNameInput(e: Event): void {
  builder.setName((e.target as HTMLInputElement).value)
}

function onDescriptionInput(e: Event): void {
  builder.setDescription((e.target as HTMLTextAreaElement).value)
}
</script>

<template>
  <FieldFrame
    :label="props.label"
    :hide-label="props.hideLabel"
    :help-text="props.helpText"
    :errors="props.errors"
    :class="props.classList"
  >
    <Card
      class="gxp-preset-builder"
      data-testid="preset-builder"
      :data-element-name="props.name ?? undefined"
    >
      <CardHeader>
        <div class="flex items-center justify-between gap-2">
          <CardTitle class="text-base">Preset</CardTitle>
          <Badge variant="outline" class="font-mono text-[10px]">
            builder/preset-builder
          </Badge>
        </div>
      </CardHeader>
      <CardContent class="flex flex-col gap-4">
        <div class="grid gap-3 sm:grid-cols-2">
          <div class="flex flex-col gap-1">
            <Label for="preset-builder-name">Preset name</Label>
            <Input
              id="preset-builder-name"
              :model-value="stateRef.presetName"
              placeholder="My preset"
              data-testid="preset-builder-name"
              @input="onNameInput"
            />
          </div>
          <div class="flex flex-col gap-1">
            <Label for="preset-builder-description">Description</Label>
            <Textarea
              id="preset-builder-description"
              :model-value="stateRef.presetDescription"
              placeholder="Optional description"
              data-testid="preset-builder-description"
              @input="onDescriptionInput"
            />
          </div>
        </div>

        <div
          :class="cn('rounded-md border bg-card/40 p-2')"
          data-testid="preset-builder-inner"
        >
          <FormBuilder
            :builder-id="innerBuilderId"
            :item="props.item"
          />
        </div>
      </CardContent>
    </Card>
  </FieldFrame>
</template>
