<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useApi } from '@/composables/useApi'
import { useAuthStore } from '@/stores/auth'
import { useToastStore } from '@/stores/toast'
import type { MapEntry } from '@/types'
import { safeFileSize } from '@/utils/formatters'
import Panel from '@/components/ds/data/Panel.vue'
import Button from '@/components/ds/core/Button.vue'
import Badge from '@/components/ds/core/Badge.vue'
import IconButton from '@/components/ds/core/IconButton.vue'
import EmptyState from '@/components/ds/feedback/EmptyState.vue'

const api = useApi()
const auth = useAuthStore()
const toast = useToastStore()

const maps = ref<MapEntry[]>([])
const isLoading = ref(false)
const isUploading = ref(false)
const fileInputRef = ref<HTMLInputElement | null>(null)

function formatSize(bytes: number): string {
  return safeFileSize(bytes)
}

function mapTypeTone(type: string): 'accent' | 'info' {
  return type === 'custom' ? 'accent' : 'info'
}

async function fetchMaps() {
  isLoading.value = true
  try {
    const data = await api.get<{ maps: MapEntry[] }>('/maps')
    maps.value = data.maps
  } catch {
    toast.error('Failed to load map library')
  } finally {
    isLoading.value = false
  }
}

async function deleteMap(map: MapEntry) {
  if (!confirm(`Delete ${map.display_name}?`)) return
  try {
    await api.del(`/maps/${map.id}`)
    await fetchMaps()
    toast.success(`${map.display_name} deleted`)
  } catch (err) {
    toast.error(err instanceof Error ? err.message : 'Failed to delete map')
  }
}

function triggerFileInput() {
  fileInputRef.value?.click()
}

async function handleFileSelected(event: Event) {
  const input = event.target as HTMLInputElement
  const file = input.files?.[0]
  if (!file) return

  // Reset the input so the same file can be re-selected if needed
  input.value = ''

  isUploading.value = true
  try {
    const formData = new FormData()
    formData.append('file', file)

    const response = await fetch('/api/maps', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${auth.accessToken}`,
      },
      body: formData,
    })

    if (!response.ok) {
      const err = await response.json().catch(() => ({ message: 'Upload failed' }))
      throw new Error(err.message || `HTTP ${response.status}`)
    }

    toast.success(`${file.name} uploaded successfully`)
    await fetchMaps()
  } catch (err) {
    toast.error(err instanceof Error ? err.message : 'Upload failed')
  } finally {
    isUploading.value = false
  }
}

onMounted(() => {
  fetchMaps()
})
</script>

<template>
  <div class="maps">
    <!-- Page head -->
    <div class="page__head">
      <div>
        <div class="t-eyebrow">Operations</div>
        <h1 class="page__title">
          Map library
          <span v-if="maps.length > 0" class="page__count">{{ maps.length }} maps</span>
        </h1>
      </div>
      <div class="page__actions">
        <IconButton
          icon="refresh-cw"
          label="Refresh"
          :class="isLoading && 'spin'"
          :disabled="isLoading"
          @click="fetchMaps"
        />
        <input
          ref="fileInputRef"
          type="file"
          accept=".map"
          class="hidden-input"
          @change="handleFileSelected"
        />
        <Button
          icon="upload"
          :loading="isUploading"
          @click="triggerFileInput"
        >
          {{ isUploading ? 'Uploading…' : 'Upload map' }}
        </Button>
      </div>
    </div>

    <!-- Empty state -->
    <Panel v-if="maps.length === 0">
      <EmptyState
        icon="map"
        title="No maps"
        description="Upload custom maps or they will appear here when procedural maps are generated."
      >
        <template #action>
          <Button icon="upload" size="sm" @click="triggerFileInput">Upload map</Button>
        </template>
      </EmptyState>
    </Panel>

    <!-- Maps grid -->
    <div v-else class="maps-grid">
      <div
        v-for="map in maps"
        :key="map.id"
        class="map-card"
      >
        <!-- Thumbnail -->
        <div class="map-card__thumb">
          <img
            v-if="map.thumbnail_path"
            :src="map.thumbnail_path"
            :alt="map.display_name"
            class="map-card__img"
          />
          <svg v-else class="map-card__placeholder" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
            <polygon points="3 6 9 3 15 6 21 3 21 18 15 21 9 18 3 21" /><line x1="9" y1="3" x2="9" y2="18" /><line x1="15" y1="6" x2="15" y2="21" />
          </svg>
        </div>

        <!-- Card body -->
        <div class="map-card__body">
          <div class="map-card__row">
            <span class="map-card__name">{{ map.display_name }}</span>
            <Badge :tone="mapTypeTone(map.map_type)" size="md">{{ map.map_type }}</Badge>
          </div>
          <div class="map-card__meta">
            <span>{{ formatSize(map.file_size_bytes) }}</span>
            <span v-if="map.world_size">{{ map.world_size }}m</span>
            <span v-if="map.seed" class="mono">Seed: {{ map.seed }}</span>
          </div>
          <div class="map-card__foot">
            <IconButton
              icon="trash-2"
              variant="danger"
              size="sm"
              label="Delete map"
              @click="deleteMap(map)"
            />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.maps {
  max-width: 1100px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* Page head */
.page__head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.page__title {
  font-size: var(--text-3xl);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  margin-top: 4px;
  display: flex;
  align-items: baseline;
  gap: 10px;
}
.page__count {
  font-size: var(--text-sm);
  font-weight: 400;
  color: var(--text-tertiary);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
.page__actions {
  display: flex;
  align-items: center;
  gap: 8px;
}
.hidden-input { display: none; }

/* Spin utility */
.spin { animation: spin 0.7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Maps grid */
.maps-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 14px;
}

/* Map card */
.map-card {
  background: var(--surface-base);
  border-radius: var(--radius-lg);
  box-shadow: var(--ring-default);
  overflow: hidden;
  transition: box-shadow var(--dur-base) var(--ease-standard);
}
.map-card:hover {
  box-shadow: var(--ring-default), 0 0 0 1px var(--border-default);
}
.map-card__thumb {
  height: 128px;
  background: var(--surface-inset);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.map-card__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.map-card__placeholder { color: var(--text-muted); }
.map-card__body { padding: 12px 14px; }
.map-card__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 5px;
}
.map-card__name {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
  min-width: 0;
}
.map-card__meta {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-xs);
  color: var(--text-tertiary);
  flex-wrap: wrap;
}
.mono { font-family: var(--font-mono); }
.map-card__foot {
  display: flex;
  justify-content: flex-end;
  margin-top: 8px;
}

@media (max-width: 640px) {
  .maps-grid { grid-template-columns: 1fr 1fr; }
}
</style>
