<template>
  <div>
    <div class="page-header">
      <h2>Floor Maps</h2>
      <button class="btn btn-secondary" @click="showAddBuilding = true">Add building</button>
    </div>

    <p class="setting-description">
      Each level is one drawing with its own blueprint and its own pixel size.
      Marker positions are pixels in the level's own space, so the level id below
      is what a position belongs to - it is worth knowing when you run a
      transform or ask about a marker that is in the wrong place.
    </p>

    <div v-if="loading" class="empty">Loading...</div>

    <div v-for="building in buildings" :key="building.buildingid" class="section-card">
      <div class="building-header">
        <input
          v-model="building.buildingname"
          class="building-name"
          @blur="renameBuilding(building)"
          :disabled="saving"
        />
        <span class="muted">{{ building.levels.length }} level(s)</span>
        <button class="btn btn-small" @click="startAddLevel(building)">Add level</button>
      </div>

      <table class="data-table">
        <thead>
          <tr>
            <th title="What a marker position on this level refers to">Level id</th>
            <th>Name</th>
            <th>Order</th>
            <th>Native size</th>
            <th>Markers</th>
            <th>Light</th>
            <th>Dark</th>
            <th>Default</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="level in building.levels" :key="level.levelid">
            <td><code class="levelid">{{ level.levelid }}</code></td>
            <td>
              <input
                v-model="level.levelname"
                class="form-control"
                @blur="renameLevel(level)"
                :disabled="saving"
              />
            </td>
            <td>
              <input
                v-model.number="level.sortorder"
                type="number"
                class="form-control order-input"
                @blur="saveLevel(level, { sortorder: level.sortorder })"
                :disabled="saving"
                title="Lower sorts first. Ground 0, first floor 1, mezzanine 5 between them later."
              />
            </td>
            <td>
              <div class="size-fields">
                <input
                  v-model.number="level.mapwidth"
                  type="number"
                  min="1"
                  class="form-control size-input"
                  aria-label="Blueprint width in pixels"
                  @blur="saveSize(level)"
                  :disabled="saving"
                />
                <span class="size-x">x</span>
                <input
                  v-model.number="level.mapheight"
                  type="number"
                  min="1"
                  class="form-control size-input"
                  aria-label="Blueprint height in pixels"
                  @blur="saveSize(level)"
                  :disabled="saving"
                />
              </div>
              <small v-if="level.assetcount" class="input-hint">
                Taken from the image while a level is empty. Changing it now
                re-scales where all {{ level.assetcount }} marker(s) sit relative
                to the drawing - set the new size, then Recalibrate from
                landmarks in the map editor.
              </small>
              <small v-else class="input-hint">
                Set from the blueprint you upload.
              </small>
            </td>
            <td>
              <span :class="{ 'muted': !level.assetcount }">{{ level.assetcount }}</span>
            </td>
            <td>
              <img v-if="level.blueprintlight" :src="withBase(level.blueprintlight)"
                   class="map-thumb" alt="light blueprint" />
              <input type="file" accept="image/*" class="file-input"
                     @change="upload(level, 'light', $event)" :disabled="uploading" />
            </td>
            <td>
              <img v-if="level.blueprintdark" :src="withBase(level.blueprintdark)"
                   class="map-thumb map-thumb-dark" alt="dark blueprint" />
              <input type="file" accept="image/*" class="file-input"
                     @change="upload(level, 'dark', $event)" :disabled="uploading" />
            </td>
            <td>
              <input type="radio" :checked="level.isdefault" name="defaultlevel"
                     @change="saveLevel(level, { isdefault: true })"
                     title="Where an asset with no level lands, and what the map opens on" />
            </td>
            <td class="actions">
              <button class="btn btn-small btn-danger" @click="remove(level)"
                      :disabled="saving">Remove</button>
            </td>
          </tr>
          <tr v-if="!building.levels.length">
            <td colspan="9" class="empty">No levels yet. Add one, then upload its blueprint.</td>
          </tr>
        </tbody>
      </table>
    </div>

    <!-- Add building -->
    <div v-if="showAddBuilding" class="modal-overlay">
      <div class="modal">
        <div class="modal-header"><h3>Add building</h3></div>
        <div class="modal-body">
          <div class="form-group">
            <label>Name</label>
            <input v-model="newBuilding" class="form-control" placeholder="Annex"
                   @keyup.enter="addBuilding" />
          </div>
        </div>
        <div class="modal-footer">
          <button class="btn btn-secondary" @click="showAddBuilding = false">Cancel</button>
          <button class="btn btn-primary" @click="addBuilding" :disabled="!newBuilding.trim()">Add</button>
        </div>
      </div>
    </div>

    <!-- Add level -->
    <div v-if="addLevelFor" class="modal-overlay">
      <div class="modal">
        <div class="modal-header"><h3>Add level to {{ addLevelFor.buildingname }}</h3></div>
        <div class="modal-body">
          <div class="form-group">
            <label>Name</label>
            <input v-model="newLevel.levelname" class="form-control"
                   placeholder="Second floor" @keyup.enter="addLevel" />
            <small class="input-hint">
              Whatever the building calls it. Basement, Ground, Mezzanine, Roof.
            </small>
          </div>
          <div class="form-group">
            <label>Sort order</label>
            <input v-model.number="newLevel.sortorder" type="number" class="form-control" />
            <small class="input-hint">
              Lower sorts first, and gaps are fine - leaving room lets a mezzanine
              slot in later without renumbering anything.
            </small>
          </div>
          <p class="input-hint">
            Upload the blueprint after creating it. An empty level takes its pixel
            size from the image, so there is nothing to measure by hand.
          </p>
        </div>
        <div class="modal-footer">
          <button class="btn btn-secondary" @click="addLevelFor = null">Cancel</button>
          <button class="btn btn-primary" @click="addLevel"
                  :disabled="!newLevel.levelname.trim()">Add</button>
        </div>
      </div>
    </div>

    <div v-if="error" class="error-message">{{ error }}</div>
    <div v-if="notice" class="settings-success">{{ notice }}</div>
  </div>
</template>

<script setup>
// Buildings and levels admin (ADR-017). This page replaced four site-wide
// settings that described a single blueprint, which could not express a second
// level drawn at a different size, let alone a second building.
//
// The level id is deliberately on screen. It is what `assets.levelid` points at,
// what a landmark transform takes as an argument, and the first thing worth
// knowing when a marker draws on the wrong drawing.
import { ref, onMounted } from 'vue'

import { mapLevelsApi } from '@/api'
import { withBase } from '@/utils/basePath'
import { reloadMapConfig } from '@/composables/mapConfig'

const buildings = ref([])
const loading = ref(true)
const saving = ref(false)
const uploading = ref(false)
const error = ref('')
const notice = ref('')

const showAddBuilding = ref(false)
const newBuilding = ref('')
const addLevelFor = ref(null)
const newLevel = ref({ levelname: '', sortorder: 0 })

onMounted(load)

async function load() {
  loading.value = true
  try {
    const { data } = await mapLevelsApi.list()
    buildings.value = data.data.buildings || []
  } catch (err) {
    error.value = message(err, 'Could not load the levels')
  } finally {
    loading.value = false
  }
}

function message(err, fallback) {
  return err?.response?.data?.data?.error?.message || fallback
}

function report(text) {
  notice.value = text
  error.value = ''
  // Long enough to read a sentence about what did not happen.
  setTimeout(() => { notice.value = '' }, 8000)
}

async function addBuilding() {
  const name = newBuilding.value.trim()
  if (!name) return
  saving.value = true
  try {
    await mapLevelsApi.createBuilding({
      buildingname: name,
      sortorder: buildings.value.length,
    })
    showAddBuilding.value = false
    newBuilding.value = ''
    await load()
    report(`Building "${name}" added. Add its levels next.`)
  } catch (err) {
    error.value = message(err, 'Could not add the building')
  } finally {
    saving.value = false
  }
}

function startAddLevel(building) {
  addLevelFor.value = building
  // Default to one past the last, so the common case needs no thought and the
  // uncommon one is still editable.
  newLevel.value = {
    levelname: '',
    sortorder: (building.levels.at(-1)?.sortorder ?? -1) + 1,
  }
}

async function addLevel() {
  const name = newLevel.value.levelname.trim()
  if (!name || !addLevelFor.value) return
  saving.value = true
  try {
    const { data } = await mapLevelsApi.create({
      buildingid: addLevelFor.value.buildingid,
      levelname: name,
      sortorder: newLevel.value.sortorder,
    })
    addLevelFor.value = null
    await load()
    report(`"${name}" created as level ${data.data.levelid}. Upload its blueprint to set its size.`)
  } catch (err) {
    error.value = message(err, 'Could not add the level')
  } finally {
    saving.value = false
  }
}

async function renameBuilding(building) {
  const name = (building.buildingname || '').trim()
  if (!name) {
    await load()
    return
  }
  saving.value = true
  try {
    await mapLevelsApi.updateBuilding(building.buildingid, { buildingname: name })
    report(`Building renamed to "${name}".`)
  } catch (err) {
    error.value = message(err, 'Could not rename the building')
    await load()
  } finally {
    saving.value = false
  }
}

async function renameLevel(level) {
  const name = (level.levelname || '').trim()
  if (!name) {
    await load()
    return
  }
  await saveLevel(level, { levelname: name })
}

async function saveLevel(level, payload) {
  saving.value = true
  try {
    const { data } = await mapLevelsApi.update(level.levelid, payload)
    // The server reports when a change leaves existing positions in an old
    // coordinate space. Surfacing that verbatim matters more than a tidy
    // message: it names the markers that are now wrong.
    const warnings = data.data?.warnings
    await load()
    await reloadMapConfig()
    report(warnings?.length ? warnings.join(' ') : 'Saved.')
  } catch (err) {
    error.value = message(err, 'Could not save the level')
    await load()
  } finally {
    saving.value = false
  }
}

async function saveSize(level) {
  if (!(level.mapwidth > 0) || !(level.mapheight > 0)) {
    error.value = 'Width and height must both be positive.'
    await load()
    return
  }
  // A level with markers keeps its coordinates when the size changes, so every
  // marker moves relative to the drawing. That is sometimes exactly right (a
  // re-export of the same plan at a new resolution) and sometimes the start of
  // a recalibration, but it is never something to do by accident.
  if (level.assetcount) {
    const ok = window.confirm(
      `${level.levelname} has ${level.assetcount} marker(s) placed against ` +
      `${level.mapwidth} x ${level.mapheight}. Changing the size moves all of ` +
      `them relative to the drawing. Recalibrate from landmarks afterwards. Continue?`)
    if (!ok) {
      await load()
      return
    }
  }
  await saveLevel(level, { mapwidth: level.mapwidth, mapheight: level.mapheight })
}

async function upload(level, theme, event) {
  const file = event.target.files?.[0]
  if (!file) return
  uploading.value = true
  try {
    const { data } = await mapLevelsApi.uploadBlueprint(level.levelid, theme, file)
    await load()
    await reloadMapConfig()
    // sizenote is the interesting case: the image disagrees with the stored
    // dimensions AND markers are already placed, so the server refused to
    // change the coordinate space out from under them.
    report(data.data.sizenote
      || `Blueprint uploaded (${data.data.detectedwidth} x ${data.data.detectedheight}).`)
  } catch (err) {
    error.value = message(err, 'Could not upload the blueprint')
  } finally {
    uploading.value = false
    event.target.value = ''
  }
}

async function remove(level) {
  const placed = level.assetcount
    ? ` It has ${level.assetcount} marker(s) on it, which the server will refuse.`
    : ''
  if (!confirm(`Remove "${level.levelname}" (level ${level.levelid})?${placed}`)) return
  saving.value = true
  try {
    await mapLevelsApi.remove(level.levelid)
    await load()
    report(`Level ${level.levelid} removed.`)
  } catch (err) {
    error.value = message(err, 'Could not remove the level')
  } finally {
    saving.value = false
  }
}
</script>

<style scoped>
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.setting-description {
  color: var(--text-light);
  max-width: 60rem;
  margin-bottom: 1rem;
}

.building-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.building-name {
  font-size: 1.05rem;
  font-weight: 600;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 0.25rem 0.4rem;
  color: var(--text);
}

.building-name:hover,
.building-name:focus {
  border-color: var(--border);
  background: var(--bg);
}

.levelid {
  font-family: monospace;
  font-size: 0.95rem;
  padding: 0.1rem 0.4rem;
  background: var(--bg);
  border-radius: 3px;
}

.mono { font-family: monospace; }
.order-input { width: 4.5rem; }
.size-input { width: 6rem; }
.size-fields { display: flex; align-items: center; gap: 0.35rem; }
.size-x { color: var(--text-light); }
.file-input { display: block; margin-top: 0.25rem; font-size: 0.75rem; }

.map-thumb {
  max-width: 5rem;
  max-height: 3rem;
  border: 1px solid var(--border);
  display: block;
}

.map-thumb-dark { background: #222; }

.muted { color: var(--text-light); }
.empty { color: var(--text-light); text-align: center; padding: 1rem; }
.input-hint { display: block; color: var(--text-light); font-size: 0.75rem; }

</style>
