<script setup lang="ts">
import { ref, computed, watch, onMounted, onUnmounted, defineAsyncComponent } from 'vue'
import { EntityObject } from '@DocumentsManager/models/entity'
import { useFileViewer } from '@/composables'

const props = defineProps<{
  modelValue: boolean
  entity: EntityObject | null
  /** All files in the current folder, used for prev/next navigation */
  allEntities: EntityObject[]
  /** Converts an entity into a URL usable as an img/video src */
  getUrl: (entityId: string, collectionId: string | null) => string
  /** Called to trigger a browser download of an entity */
  downloadEntity: (entityId: string, collectionId: string | null) => void
}>()

const emit = defineEmits<{
  'update:modelValue': [value: boolean]
  /** Request the parent to switch to a different entity */
  'navigate': [entity: EntityObject]
}>()

const fileViewer = useFileViewer()

// ── Derived state ──────────────────────────────────────────────────────────

const url = computed(() => {
  if (!props.entity) return ''
  return props.getUrl(
    String(props.entity.identifier ?? ''),
    props.entity.collection ? String(props.entity.collection) : null,
  )
})

const mime = computed(() => props.entity?.properties.mime ?? '')

const viewer = computed(() => {
  if (!mime.value) return null
  return fileViewer.findViewer(mime.value)
})

// Wrap the raw () => import() factory in defineAsyncComponent so Vue
// actually resolves it instead of rendering the Promise as text.
const viewerComponent = computed(() => {
  if (!viewer.value?.component) return null
  return defineAsyncComponent(viewer.value.component as () => Promise<unknown>)
})

const filename = computed(
  () => props.entity?.properties.label ?? String(props.entity?.identifier ?? ''),
)

const currentIndex = computed(() => {
  if (!props.entity) return -1
  return props.allEntities.findIndex(e => e.identifier === props.entity!.identifier)
})

const hasPrev = computed(() => currentIndex.value > 0)
const hasNext = computed(() => currentIndex.value < props.allEntities.length - 1)

// Viewer component may take a moment to load; track async state
const viewerLoading = ref(false)

watch(() => props.entity, () => {
  viewerLoading.value = false
})

// ── Navigation ──────────────────────────────────────────────────────────────

function navigatePrev() {
  if (!hasPrev.value) return
  emit('navigate', props.allEntities[currentIndex.value - 1])
}

function navigateNext() {
  if (!hasNext.value) return
  emit('navigate', props.allEntities[currentIndex.value + 1])
}

function close() {
  emit('update:modelValue', false)
}

function handleDownload() {
  if (!props.entity) return
  props.downloadEntity(
    String(props.entity.identifier ?? ''),
    props.entity.collection ? String(props.entity.collection) : null,
  )
}

// ── Keyboard shortcuts ──────────────────────────────────────────────────────

function handleKeydown(e: KeyboardEvent) {
  if (!props.modelValue) return
  if (e.key === 'ArrowLeft') { e.preventDefault(); navigatePrev() }
  else if (e.key === 'ArrowRight') { e.preventDefault(); navigateNext() }
  else if (e.key === 'Escape') { e.preventDefault(); close() }
}

onMounted(() => window.addEventListener('keydown', handleKeydown))
onUnmounted(() => window.removeEventListener('keydown', handleKeydown))
</script>

<template>
  <v-dialog
    :model-value="modelValue"
    fullscreen
    transition="dialog-bottom-transition"
    @update:model-value="emit('update:modelValue', $event)"
  >
    <div class="viewer-shell">

      <!-- ── Toolbar ─────────────────────────────────────────────────── -->
      <div class="viewer-toolbar">
        <v-btn icon="mdi-close" variant="text" @click="close" />

        <span class="viewer-filename text-truncate">{{ filename }}</span>

        <div class="viewer-toolbar-actions">
          <span v-if="allEntities.length > 1" class="viewer-counter text-caption text-medium-emphasis mr-2">
            {{ currentIndex + 1 }} / {{ allEntities.length }}
          </span>
          <v-btn
            icon="mdi-download"
            variant="text"
            size="small"
            title="Download"
            @click="handleDownload"
          />
        </div>
      </div>

      <!-- ── Content area ───────────────────────────────────────────── -->
      <div class="viewer-content">

        <!-- Prev arrow -->
        <div class="viewer-nav viewer-nav--prev">
          <v-btn
            v-if="hasPrev"
            icon="mdi-chevron-left"
            variant="elevated"
            size="large"
            @click="navigatePrev"
          />
        </div>

        <!-- Viewer / fallback -->
        <div class="viewer-stage">
          <template v-if="entity">
            <!-- Registered viewer component -->
            <Suspense v-if="viewer">
              <template #default>
                <component
                  :is="viewerComponent"
                  :url="url"
                  :entity="entity"
                  :mime="mime"
                  class="viewer-component"
                />
              </template>
              <template #fallback>
                <div class="viewer-loading">
                  <v-progress-circular indeterminate color="primary" />
                </div>
              </template>
            </Suspense>

            <!-- No viewer registered for this type -->
            <div v-else class="viewer-no-preview">
              <v-icon size="64" color="grey-lighten-1">mdi-file-question-outline</v-icon>
              <p class="text-h6 mt-4">No preview available</p>
              <p class="text-body-2 text-medium-emphasis mb-6">
                {{ mime || 'Unknown file type' }}
              </p>
              <v-btn
                prepend-icon="mdi-download"
                variant="tonal"
                @click="handleDownload"
              >
                Download file
              </v-btn>
            </div>
          </template>
        </div>

        <!-- Next arrow -->
        <div class="viewer-nav viewer-nav--next">
          <v-btn
            v-if="hasNext"
            icon="mdi-chevron-right"
            variant="elevated"
            size="large"
            @click="navigateNext"
          />
        </div>

      </div>
    </div>
  </v-dialog>
</template>

<style scoped>
.viewer-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: rgb(var(--v-theme-surface));
}

/* Toolbar */
.viewer-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 8px;
  border-bottom: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
  min-height: 56px;
  flex-shrink: 0;
}

.viewer-filename {
  flex: 1;
  font-size: 0.9375rem;
  font-weight: 500;
}

.viewer-toolbar-actions {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

/* Content */
.viewer-content {
  flex: 1;
  display: flex;
  align-items: stretch;
  overflow: hidden;
}

/* Prev/Next nav columns */
.viewer-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  flex-shrink: 0;
  padding: 8px;
}

/* Main stage */
.viewer-stage {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: auto;
  position: relative;
}

.viewer-component {
  max-width: 100%;
  max-height: 100%;
}

/* Loading */
.viewer-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-height: 200px;
}

/* No-preview fallback */
.viewer-no-preview {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 32px;
}

@media (max-width: 600px) {
  .viewer-nav {
    width: 40px;
    padding: 4px;
  }
}
</style>
