<template>
  <div class="container mx-auto p-4 max-w-2xl min-w-0">
    <h1 class="text-2xl font-bold mb-6">Admin</h1>

    <template v-if="loading">
      <div class="card card-app card-bg-base-200 mb-6">
        <div class="card-body">
          <Skeleton class="h-6 w-24 mb-4" />
          <div class="flex flex-col gap-2">
            <Skeleton v-for="i in 3" :key="i" class="h-12 w-full" />
            <Skeleton class="h-9 w-28 mt-2" />
          </div>
        </div>
      </div>
      <div class="card card-app card-bg-base-200 mb-6">
        <div class="card-body">
          <Skeleton class="h-6 w-20 mb-4" />
          <div class="overflow-x-auto">
            <Skeleton class="h-32 w-full" />
          </div>
        </div>
      </div>
      <div class="card card-app card-bg-base-200 mb-6">
        <div class="card-body">
          <Skeleton class="h-6 w-16 mb-4" />
          <div class="flex flex-col gap-3">
            <Skeleton class="h-10 w-48" />
            <Skeleton class="h-10 w-48" />
            <Skeleton class="h-8 w-36" />
          </div>
        </div>
      </div>
    </template>

    <template v-else>
    <!-- Stats -->
    <div class="grid grid-cols-2 sm:grid-cols-4 gap-3 mb-6">
      <div class="stat bg-base-200 rounded-xl shadow border border-base-300/50 py-4 px-4">
        <div class="stat-title text-xs">Users</div>
        <div class="stat-value text-2xl">{{ users.length }}</div>
      </div>
      <div class="stat bg-base-200 rounded-xl shadow border border-base-300/50 py-4 px-4">
        <div class="stat-title text-xs">Maps</div>
        <div class="stat-value text-2xl">{{ maps.length }}</div>
      </div>
    </div>

    <div class="card card-app card-bg-base-200 mb-6 transition-all duration-200">
      <div class="card-body">
        <h2 class="card-title gap-2">
          <icons-icon-users />
          Users
        </h2>
        <div class="form-control w-full mb-3 sm:max-w-xs">
          <input
            v-model="userSearch"
            type="search"
            placeholder="Search users…"
            class="input input-sm input-bordered w-full min-h-11 touch-manipulation"
            aria-label="Search users"
          />
        </div>
          <div class="flex flex-col gap-2 max-h-[60vh] overflow-y-auto">
            <div
              v-for="u in filteredUsers"
              :key="u"
              class="flex items-center justify-between gap-3 w-full p-3 rounded-lg bg-base-300/50 hover:bg-base-300/70 transition-colors shrink-0"
            >
              <div class="flex items-center gap-2 min-w-0">
                <div class="avatar avatar-placeholder">
                  <div class="bg-neutral text-neutral-content rounded-full w-8">
                    <span class="text-xs">{{ u[0]?.toUpperCase() }}</span>
                  </div>
                </div>
                <span class="font-medium truncate">{{ u }}</span>
              </div>
              <NuxtLink :to="`/admin/users/${u}`" class="btn btn-outline btn-sm gap-1 shrink-0 min-h-11 touch-manipulation">
                <icons-icon-pencil /> Edit
              </NuxtLink>
            </div>
            <div v-if="!filteredUsers.length" class="py-6 text-center text-base-content/60 rounded-lg bg-base-300/30">
              {{ userSearch ? 'No users match.' : 'No users yet.' }}
            </div>
            <NuxtLink to="/admin/users/new" class="btn btn-primary btn-sm mt-2 gap-1 min-h-11 touch-manipulation">
              <icons-icon-plus /> Add user
            </NuxtLink>
          </div>
        </div>
    </div>

    <div class="card card-app card-bg-base-200 mb-6 transition-all duration-200">
      <div class="card-body">
        <h2 class="card-title gap-2">
          <icons-icon-map-pin />
          Maps
        </h2>
        <div class="form-control w-full mb-3 sm:max-w-xs">
          <input
            v-model="mapSearch"
            type="search"
            placeholder="Search maps…"
            class="input input-sm input-bordered w-full min-h-11 touch-manipulation"
            aria-label="Search maps"
          />
        </div>
          <div class="overflow-x-auto max-h-[60vh] overflow-y-auto">
            <table class="table table-sm table-zebra min-w-[32rem]">
              <thead>
                <tr>
                  <th scope="col">
                    <button type="button" class="btn btn-ghost btn-xs gap-0.5 min-h-9 touch-manipulation" :class="mapsSortBy === 'ID' ? 'btn-active' : ''" @click="setMapsSort('ID')">
                      ID
                      <span v-if="mapsSortBy === 'ID'" class="opacity-70">{{ mapsSortDir === 'asc' ? '↑' : '↓' }}</span>
                    </button>
                  </th>
                  <th scope="col">
                    <button type="button" class="btn btn-ghost btn-xs gap-0.5 min-h-9 touch-manipulation" :class="mapsSortBy === 'Name' ? 'btn-active' : ''" @click="setMapsSort('Name')">
                      Name
                      <span v-if="mapsSortBy === 'Name'" class="opacity-70">{{ mapsSortDir === 'asc' ? '↑' : '↓' }}</span>
                    </button>
                  </th>
                  <th scope="col">Hidden</th>
                  <th scope="col">Priority</th>
                  <th scope="col" class="text-right"></th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="map in sortedFilteredMaps" :key="map.ID" class="hover:bg-base-300">
                  <td>{{ map.ID }}</td>
                  <td>{{ map.Name }}</td>
                  <td>{{ map.Hidden ? 'Yes' : 'No' }}</td>
                  <td>{{ map.Priority ? 'Yes' : 'No' }}</td>
                  <td class="text-right">
                    <NuxtLink :to="`/admin/maps/${map.ID}`" class="btn btn-outline btn-sm gap-1 min-h-9 touch-manipulation">
                      <icons-icon-pencil /> Edit
                    </NuxtLink>
                  </td>
                </tr>
                <tr v-if="!sortedFilteredMaps.length">
                  <td colspan="5" class="text-base-content/60">{{ mapSearch ? 'No maps match.' : 'No maps.' }}</td>
                </tr>
              </tbody>
            </table>
          </div>
      </div>
    </div>

    <div class="card card-app card-bg-base-200 mb-6 transition-all duration-200">
      <div class="card-body">
        <h2 class="card-title gap-2">
          <icons-icon-settings />
          Settings
        </h2>
          <div class="flex flex-col gap-4">
            <fieldset class="fieldset w-full max-w-xs">
              <label class="label" for="admin-settings-prefix">Prefix</label>
              <input
                id="admin-settings-prefix"
                v-model="settings.prefix"
                type="text"
                class="input input-sm w-full min-h-11 touch-manipulation"
              />
            </fieldset>
            <fieldset class="fieldset w-full max-w-xs">
              <label class="label" for="admin-settings-title">Title</label>
              <input
                id="admin-settings-title"
                v-model="settings.title"
                type="text"
                class="input input-sm w-full min-h-11 touch-manipulation"
              />
            </fieldset>
            <fieldset class="fieldset">
              <label class="label gap-2 cursor-pointer justify-start min-h-11 touch-manipulation" for="admin-settings-default-hide">
                <input
                  id="admin-settings-default-hide"
                  v-model="settings.defaultHide"
                  type="checkbox"
                  class="checkbox checkbox-sm"
                />
                Default hide new maps
              </label>
            </fieldset>
          </div>
          <div class="flex justify-end mt-2">
            <button class="btn btn-primary btn-sm min-h-11 touch-manipulation" :disabled="savingSettings" @click="saveSettings">
              <span v-if="savingSettings" class="loading loading-spinner loading-sm" />
              <span v-else>Save settings</span>
            </button>
          </div>
        </div>
    </div>

    <div class="card card-app card-bg-base-200 mb-6 transition-all duration-200">
      <div class="card-body">
        <h2 class="card-title gap-2">
          <icons-icon-alert-triangle />
          Actions
        </h2>
          <div class="flex flex-col gap-3">
            <div>
              <a :href="exportUrl" target="_blank" rel="noopener" class="btn btn-sm min-h-11 touch-manipulation">
                Export zip
              </a>
            </div>
            <div>
              <button class="btn btn-sm min-h-11 touch-manipulation" :disabled="rebuilding" @click="confirmRebuildZooms">
                <span v-if="rebuilding" class="loading loading-spinner loading-sm" />
                <span v-else>Rebuild zooms</span>
              </button>
            </div>
            <div class="flex flex-col gap-2">
              <div class="flex flex-wrap items-center gap-2">
                <input ref="mergeFileRef" type="file" accept=".zip" class="hidden" @change="onMergeFile" />
                <button type="button" class="btn btn-sm min-h-11 touch-manipulation" @click="mergeFileRef?.click()">
                  Choose merge file
                </button>
                <span class="text-sm text-base-content/70">
                  {{ mergeFile ? mergeFile.name : 'No file chosen' }}
                </span>
              </div>
              <form @submit.prevent="doMerge">
                <button type="submit" class="btn btn-sm btn-primary min-h-11 touch-manipulation" :disabled="!mergeFile || merging">
                  <span v-if="merging" class="loading loading-spinner loading-sm" />
                  <span v-else>Merge</span>
                </button>
              </form>
            </div>
            <div class="border-t border-error/30 pt-4 mt-1 bg-error/5 rounded-lg p-3 -mx-1">
              <p class="text-sm font-medium text-error mb-2">Danger zone</p>
              <button class="btn btn-sm btn-error min-h-11 touch-manipulation" :disabled="wiping" @click="confirmWipe">
                <span v-if="wiping" class="loading loading-spinner loading-sm" />
                <span v-else>Wipe all data</span>
              </button>
            </div>
          </div>
      </div>
    </div>

    <!-- Progress indicator for long operations -->
    <div
      v-if="rebuilding || merging || wiping"
      class="fixed inset-0 z-50 flex items-center justify-center bg-base-100/80 backdrop-blur-sm"
      role="status"
      aria-live="polite"
      aria-busy="true"
      :aria-label="rebuilding ? 'Rebuilding zooms in progress' : merging ? 'Merging in progress' : 'Wiping data in progress'"
    >
      <div class="flex flex-col items-center gap-4 p-6 rounded-xl bg-base-200 border border-base-300 shadow-2xl">
        <span class="loading loading-spinner loading-lg text-primary" />
        <p class="font-medium">
          {{ rebuilding ? 'Rebuilding zooms…' : merging ? 'Merging…' : 'Wiping data…' }}
        </p>
        <p class="text-sm text-base-content/70">This may take a moment.</p>
      </div>
    </div>

    <ConfirmModal
      v-model="showWipeModal"
      title="Confirm wipe"
      message="Wipe all grids, markers, tiles and maps? This cannot be undone."
      confirm-label="Wipe"
      :danger="true"
      :loading="wiping"
      @confirm="doWipe"
    />

    <ConfirmModal
      v-model="showRebuildModal"
      title="Rebuild zooms"
      message="Rebuild tile zoom levels for all maps? This may take a while."
      confirm-label="Rebuild"
      :loading="rebuilding"
      @confirm="doRebuildZooms"
    />
    </template>
  </div>
</template>

<script setup lang="ts">
import type { MapInfoAdmin } from '~/types/api'

definePageMeta({ middleware: 'admin' })
useHead({ title: 'Admin – HnH Map' })

const api = useMapApi()
const exportUrl = computed(() => api.adminExportUrl())
const toast = useToast()
const users = ref<string[]>([])
const maps = ref<MapInfoAdmin[]>([])
const settings = ref({ prefix: '', defaultHide: false, title: '' })
const savingSettings = ref(false)
const rebuilding = ref(false)
const wiping = ref(false)
const merging = ref(false)
const mergeFile = ref<File | null>(null)
const mergeFileRef = ref<HTMLInputElement | null>(null)
const showWipeModal = ref(false)
const showRebuildModal = ref(false)
const loading = ref(true)
const userSearch = ref('')
const mapSearch = ref('')
const mapsSortBy = ref<'ID' | 'Name'>('ID')
const mapsSortDir = ref<'asc' | 'desc'>('asc')

const filteredUsers = computed(() => {
  const q = userSearch.value.trim().toLowerCase()
  if (!q) return users.value
  return users.value.filter((u) => u.toLowerCase().includes(q))
})

const filteredMaps = computed(() => {
  const q = mapSearch.value.trim().toLowerCase()
  if (!q) return maps.value
  return maps.value.filter(
    (m) =>
      m.Name.toLowerCase().includes(q) ||
      String(m.ID).includes(q)
  )
})

const sortedFilteredMaps = computed(() => {
  const list = [...filteredMaps.value]
  const by = mapsSortBy.value
  const dir = mapsSortDir.value
  list.sort((a, b) => {
    let cmp = 0
    if (by === 'ID') cmp = a.ID - b.ID
    else cmp = a.Name.localeCompare(b.Name)
    return dir === 'asc' ? cmp : -cmp
  })
  return list
})

function setMapsSort(by: 'ID' | 'Name') {
  if (mapsSortBy.value === by) mapsSortDir.value = mapsSortDir.value === 'asc' ? 'desc' : 'asc'
  else {
    mapsSortBy.value = by
    mapsSortDir.value = 'asc'
  }
}

onMounted(async () => {
  await Promise.all([loadUsers(), loadMaps(), loadSettings()])
  loading.value = false
})

async function loadUsers() {
  try {
    users.value = await api.adminUsers()
  } catch {
    users.value = []
  }
}

async function loadMaps() {
  try {
    maps.value = await api.adminMaps()
  } catch {
    maps.value = []
  }
}

async function loadSettings() {
  try {
    const s = await api.adminSettings()
    settings.value = { prefix: s.prefix ?? '', defaultHide: s.defaultHide ?? false, title: s.title ?? '' }
  } catch {
    //
  }
}

async function saveSettings() {
  savingSettings.value = true
  try {
    await api.adminSettingsPost(settings.value)
    toast.success('Settings saved.')
  } catch (e) {
    toast.error((e as Error)?.message ?? 'Failed to save settings.')
  } finally {
    savingSettings.value = false
  }
}

function confirmRebuildZooms() {
  showRebuildModal.value = true
}

const { markRebuildDone } = useRebuildZoomsInvalidation()

async function doRebuildZooms() {
  rebuilding.value = true
  try {
    await api.adminRebuildZooms()
    // Rebuild runs in background; poll status until done
    const poll = async (): Promise<void> => {
      const { running } = await api.adminRebuildZoomsStatus()
      if (running) {
        await new Promise((r) => setTimeout(r, 2000))
        return poll()
      }
    }
    await poll()
    markRebuildDone()
    showRebuildModal.value = false
    toast.success('Zooms rebuilt.')
  } catch (e) {
    toast.error((e as Error)?.message ?? 'Failed to rebuild zooms.')
  } finally {
    rebuilding.value = false
  }
}

function confirmWipe() {
  showWipeModal.value = true
}

async function doWipe() {
  wiping.value = true
  try {
    await api.adminWipe()
    showWipeModal.value = false
    await loadMaps()
    toast.success('All data wiped.')
  } catch (e) {
    toast.error((e as Error)?.message ?? 'Failed to wipe.')
  } finally {
    wiping.value = false
  }
}

function onMergeFile(e: Event) {
  const input = e.target as HTMLInputElement
  mergeFile.value = input.files?.[0] ?? null
}

async function doMerge() {
  if (!mergeFile.value) return
  merging.value = true
  try {
    const fd = new FormData()
    fd.append('merge', mergeFile.value)
    await api.adminMerge(fd)
    mergeFile.value = null
    if (mergeFileRef.value) mergeFileRef.value.value = ''
    await loadMaps()
    toast.success('Merge completed.')
  } catch (e) {
    toast.error((e as Error)?.message ?? 'Merge failed.')
  } finally {
    merging.value = false
  }
}
</script>

