<template>
  <v-menu
    v-model="isOpen"
    :close-on-content-click="false"
    location="bottom end"
    offset="8"
    :max-width="520"
    :min-width="360"
  >
    <template #activator="{ props: menuProps }">
      <slot v-if="$slots.default" v-bind="{ ...triggerSlotProps, menuProps }" />
      <v-btn
        v-else
        v-bind="{ ...menuProps, ...$attrs }"
        :disabled="disabled"
        :variant="$attrs.variant || 'outlined'"
        color="primary"
        prepend-icon="mdi-export-variant"
        class="tw:font-medium tw:mb-2"
        @click="open"
      >
        {{ buttonLabel }}
      </v-btn>
    </template>

    <v-card rounded="lg" elevation="8" :style="{ width: '360px' }">
      <!-- Header -->
      <slot name="header" v-bind="headerSlotProps">
        <div class="tw:flex tw:items-center tw:justify-between tw:px-4 tw:pt-4 tw:pb-2">
          <div class="tw:flex tw:items-center tw:gap-2">
            <v-icon icon="mdi-export-variant" size="18" color="primary" />
            <span class="tw:text-sm tw:font-semibold tw:tracking-wide tw:uppercase tw:opacity-70">
              {{ $t("core.dataExporter.title") }}
            </span>
          </div>
          <v-btn icon="mdi-close" variant="text" size="x-small" @click="isOpen = false" />
        </div>
      </slot>

      <v-divider />

      <!-- Format tabs -->
      <slot name="format-selector" v-bind="formatSelectorSlotProps">
        <div class="tw:px-4 tw:pt-3 tw:pb-1">
          <v-chip-group
            v-model="activeFormatId"
            mandatory
            selected-class="text-primary"
            class="tw:gap-1"
          >
            <v-chip
              v-for="fmt in resolvedFormats"
              :key="fmt.id"
              :value="fmt.id"
              size="small"
              variant="tonal"
              class="tw:font-mono tw:text-xs"
            >
              {{ fmt.label }}
            </v-chip>
          </v-chip-group>
        </div>
      </slot>

      <!-- Data scope switch — only shown when both items AND meta exist -->
      <div
        v-if="showScopeSwitch"
        class="tw:px-4 tw:py-1 tw:flex tw:items-center tw:justify-between tw:gap-2"
      >
        <slot name="scope-switch" v-bind="scopeSwitchSlotProps">
          <span class="tw:text-xs tw:opacity-60 tw:uppercase tw:tracking-wide">
            {{ $t("core.dataExporter.scopeLabel") }}
          </span>
          <v-btn-toggle v-model="selectedScope" divided density="compact" mandatory>
            <v-btn value="combined" size="x-small">
              {{ $t("core.dataExporter.scopeCombined") }}
            </v-btn>
            <v-btn value="items" size="x-small">
              {{ $t("core.dataExporter.scopeItems") }}
            </v-btn>
            <v-btn value="meta" size="x-small">
              {{ $t("core.dataExporter.scopeMeta") }}
            </v-btn>
          </v-btn-toggle>
        </slot>
      </div>

      <!-- No formats warning -->
      <div v-if="resolvedFormats.length === 0" class="tw:px-4 tw:py-6 tw:text-center">
        <v-icon icon="mdi-alert-circle-outline" color="warning" class="tw:mb-2" />
        <p class="tw:text-sm tw:opacity-60">{{ $t("core.dataExporter.noValidFormats") }}</p>
      </div>

      <!-- Preview pane -->
      <div v-else class="tw:px-4 tw:pt-1 tw:pb-4">
        <!-- Preview toggle -->
        <slot name="preview-toggle" v-bind="previewToggleSlotProps">
          <v-btn
            variant="text"
            density="compact"
            class="tw:mb-1.5 tw:w-full tw:justify-start tw:normal-case tw:text-xs tw:opacity-60 hover:tw:opacity-90"
            @click="previewOpen = !previewOpen"
          >
            <template #prepend>
              <v-icon
                :icon="previewOpen ? 'mdi-chevron-down' : 'mdi-chevron-right'"
                size="14"
              />
            </template>
            {{ $t("core.dataExporter.preview") }}
          </v-btn>
        </slot>

        <!-- Fixed-height container prevents width reflow when toggling -->
        <div class="tw:overflow-hidden" :style="{ height: previewOpen ? 'auto' : '0px', minHeight: previewOpen ? '0px' : undefined }">
          <v-expand-transition>
            <div v-show="previewOpen">
              <slot name="preview-content" v-bind="previewContentSlotProps">
                <!-- Serialization error -->
                <v-alert
                  v-if="!serializeResult.ok"
                  type="error"
                  variant="tonal"
                  density="compact"
                  class="tw:mb-2 tw:font-mono tw:text-xs"
                >
                  <pre class="tw:whitespace-pre-wrap tw:break-all">{{ serializeResult.error }}</pre>
                </v-alert>

                <!-- Preview code block -->
                <div
                  v-else
                  class="tw:rounded-lg tw:overflow-hidden tw:border tw:border-black/10 dark:tw:border-white/10"
                >
                  <!-- Toolbar -->
                  <div
                    class="tw:flex tw:items-center tw:justify-between tw:px-3 tw:py-1.5 tw:bg-black/5 dark:tw:bg-white/5 tw:border-b tw:border-black/10 dark:tw:border-white/10"
                  >
                    <span class="tw:font-mono tw:text-[10px] tw:uppercase tw:tracking-widest tw:opacity-50">
                      {{ activeFormatId }}
                    </span>
                    <span class="tw:font-mono tw:text-[10px] tw:opacity-40">
                      {{ previewLines.lineCount }}
                      {{ $t("core.dataExporter.lines") }}
                    </span>
                  </div>

                  <!-- Code content -->
                  <pre
                    class="tw:text-xs tw:font-mono tw:p-3 tw:max-h-52 tw:overflow-auto tw:leading-relaxed tw:bg-black/[0.03] dark:tw:bg-white/[0.03] tw:m-0"
                  >{{ previewLines.text }}</pre>

                  <!-- Truncation notice -->
                  <div
                    v-if="previewLines.truncated"
                    class="tw:text-[10px] tw:text-center tw:py-1 tw:opacity-40 tw:bg-black/5 dark:tw:bg-white/5 tw:border-t tw:border-black/10 dark:tw:border-white/10"
                  >
                    {{ $t("core.dataExporter.previewTruncatedAt", { count: PREVIEW_LINE_LIMIT }) }}
                  </div>
                </div>
              </slot>
            </div>
          </v-expand-transition>
        </div>

        <!-- Action Buttons -->
        <slot name="actions" v-bind="actionsSlotProps">
          <div class="tw:flex tw:gap-2 tw:mt-3">
            <v-btn
              v-if="clipboardSupported"
              :disabled="!serializeResult.ok"
              :color="copyState === 'success' ? 'success' : copyState === 'error' ? 'error' : 'primary'"
              variant="tonal"
              size="small"
              class="tw:flex-1 tw:font-medium"
              :prepend-icon="
                copyState === 'success' ? 'mdi-check'
                : copyState === 'error' ? 'mdi-alert-circle'
                : 'mdi-content-copy'
              "
              @click="copyToClipboard"
            >
              {{
                copyState === "success" ? $t("core.dataExporter.copied")
                : copyState === "error" ? $t("core.dataExporter.copyFailed")
                : $t("core.dataExporter.copy")
              }}
            </v-btn>
            <v-btn
              :disabled="!serializeResult.ok"
              color="primary"
              variant="flat"
              size="small"
              class="tw:flex-1 tw:font-medium"
              prepend-icon="mdi-download"
              @click="downloadFile"
            >
              {{ $t("core.dataExporter.download") }}
            </v-btn>
          </div>
        </slot>
      </div>
    </v-card>
  </v-menu>
</template>

<script setup lang="ts">
import { ref, computed, watch } from "vue"
import type {
  RacletteExportProps,
  FormatEntry,
  ExportSuccessPayload,
  ExportErrorPayload,
  TriggerSlotProps,
  ExportDataScope,
} from "@racletteOrchestrator/composables/useExport/types"
import { useExport } from "@racletteOrchestrator/composables/useExport"

defineOptions({ inheritAttrs: false })

const props = withDefaults(defineProps<RacletteExportProps>(), {
  filename: "export",
  formats: () => ["json", "yaml", "csv", "text"] as FormatEntry[],
  buttonLabel: "Export",
  disabled: false,
  previewCollapsed: true,
  showDataScopeSwitch: true,
})

const emit = defineEmits<{
  "export-success": [payload: ExportSuccessPayload]
  "export-error": [payload: ExportErrorPayload]
}>()

// ─── Popover State ────────────────────────────────────────────────────────────

const isOpen = ref(false)
const previewOpen = ref(!props.previewCollapsed)

const hasItems = computed(() => props.data.items !== undefined)
const hasMeta = computed(() => props.data.meta !== undefined)

// Only show the toggle when BOTH sources exist and the prop allows it
const showScopeSwitch = computed(
  () => props.showDataScopeSwitch && hasItems.value && hasMeta.value,
)

// Derive the correct default: items if available, else meta, else combined
const defaultScope = computed<ExportDataScope>(() => {
  if (hasItems.value) return "items"
  if (hasMeta.value) return "meta"
  return "combined"
})

const selectedScope = ref<ExportDataScope>(defaultScope.value)

// Keep scope valid when data shape changes at runtime
watch(defaultScope, (next) => {
  selectedScope.value = next
})

const scopedData = computed(() => {
  if (selectedScope.value === "items") return props.data.items
  if (selectedScope.value === "meta") return props.data.meta ?? {}
  return props.data
})

const open = () => {
  if (!props.disabled) isOpen.value = true
}

// ─── Export Logic ─────────────────────────────────────────────────────────────

const {
  resolvedFormats,
  activeFormatId,
  serializeResult,
  copyToClipboard,
  downloadFile,
  copyState,
  clipboardSupported,
} = useExport({
  data: () => scopedData.value,
  formats: () => props.formats,
  defaultFormat: () => props.defaultFormat,
  filename: () => props.filename,
  enabled: () => isOpen.value,
  onSuccess: (p) => emit("export-success", p),
  onError: (p) => emit("export-error", p),
})

// ─── Trigger Slot Props ───────────────────────────────────────────────────────

const triggerSlotProps = computed<TriggerSlotProps>(() => ({
  open,
  isOpen: isOpen.value,
  disabled: props.disabled,
}))

const headerSlotProps = computed(() => ({
  isOpen: isOpen.value,
  close: () => {
    isOpen.value = false
  },
}))

const formatSelectorSlotProps = computed(() => ({
  formats: resolvedFormats.value,
  activeFormatId: activeFormatId.value,
}))

const scopeSwitchSlotProps = computed(() => ({
  selectedScope: selectedScope.value,
  showScopeSwitch: showScopeSwitch.value,
}))

const previewToggleSlotProps = computed(() => ({
  previewOpen: previewOpen.value,
  togglePreview: () => {
    previewOpen.value = !previewOpen.value
  },
}))

const previewContentSlotProps = computed(() => ({
  serializeResult: serializeResult.value,
  previewLines: previewLines.value,
  activeFormatId: activeFormatId.value,
  lineLimit: PREVIEW_LINE_LIMIT,
}))

const actionsSlotProps = computed(() => ({
  clipboardSupported: clipboardSupported.value,
  serializeResult: serializeResult.value,
  copyState: copyState.value,
  copyToClipboard,
  downloadFile,
}))

// ─── Preview lines ────────────────────────────────────────────────────────────

const PREVIEW_LINE_LIMIT = 200

const previewLines = computed<{ text: string; lineCount: number; truncated: boolean }>(() => {
  if (!serializeResult.value.ok) return { text: "", lineCount: 0, truncated: false }
  const lines = serializeResult.value.value.split("\n")
  const truncated = lines.length > PREVIEW_LINE_LIMIT
  return {
    text: truncated ? lines.slice(0, PREVIEW_LINE_LIMIT).join("\n") : serializeResult.value.value,
    lineCount: lines.length,
    truncated,
  }
})
</script>