// Which drawing renders a marker, and at what native size (ADR-017).
//
// This used to hold ONE blueprint and ONE pixel size, read from four settings,
// because a site had one floor map. It now holds every level of every building,
// because `assets.mapx`/`mapy` are pixels in a specific level's space and the
// same coordinates mean different places on different drawings.
//
// THE RULE THIS FILE ENFORCES: a position without a level is not rendered on the
// default level. `blueprintUrlFor(theme, levelid)` returns null for an unknown
// level, and every caller must show "level unknown" rather than draw something.
// Falling back would put one building's ground floor behind a marker positioned
// for another building's mezzanine - it renders perfectly and points at the
// wrong place, which is worse than rendering nothing.
import { reactive } from 'vue'

import { mapLevelsApi } from '../api'
import { withBase } from '../utils/basePath'

// Used until the levels load, and on a fresh install with none configured, so a
// map still draws something rather than breaking.
const PLACEHOLDER = '/static/images/floorplan-placeholder.svg'
const FALLBACK_WIDTH = 3300
const FALLBACK_HEIGHT = 2550

export const state = reactive({
  buildings: [],
  // Flat index by levelid, because every hover preview resolves an arbitrary
  // asset's level and has no idea which building it is in.
  levels: {},
  defaultlevelid: null,
  currentlevelid: null,
  loaded: false,
})

let inflight = null

function levelFor(levelid) {
  if (levelid === null || levelid === undefined) return null
  return state.levels[levelid] || null
}

function fetchLevels() {
  inflight = mapLevelsApi.list()
    .then(({ data }) => {
      const payload = data.data || {}
      state.buildings = payload.buildings || []
      state.levels = {}
      state.buildings.forEach(building => {
        ;(building.levels || []).forEach(level => {
          state.levels[level.levelid] = { ...level, buildingname: building.buildingname }
        })
      })
      state.defaultlevelid = payload.defaultlevelid || null
      if (!state.currentlevelid || !state.levels[state.currentlevelid]) {
        state.currentlevelid = state.defaultlevelid
      }
      state.loaded = true
    })
    .catch(() => { state.loaded = true })
    .finally(() => { inflight = null })
  return inflight
}

// Fetch once, shared across every map component. Await it before initialising a
// Leaflet map, which needs the dimensions to set its bounds.
export function loadMapConfig() {
  if (state.loaded) return Promise.resolve()
  if (inflight) return inflight
  return fetchLevels()
}

// Re-read after the levels admin changes something.
export function reloadMapConfig() {
  return fetchLevels()
}

export function setCurrentLevel(levelid) {
  if (state.levels[levelid]) state.currentlevelid = levelid
}

/**
 * Blueprint URL for one level in one theme, or null when the level is unknown.
 *
 * Falls back to the OTHER theme's image before giving up, because a site that
 * uploaded only a light blueprint should still render in dark mode - a
 * hard-to-read floor plan beats no floor plan.
 */
export function blueprintUrlFor(theme, levelid) {
  const level = levelFor(levelid === undefined ? state.currentlevelid : levelid)
  if (!level) return null
  const wanted = theme === 'light' ? level.blueprintlight : level.blueprintdark
  const other = theme === 'light' ? level.blueprintdark : level.blueprintlight
  const chosen = wanted || other
  return withBase(chosen || PLACEHOLDER)
}

/**
 * Native pixel size of a level, which is what its marker coordinates mean.
 *
 * Returns the fallback for an unknown level so arithmetic does not divide by
 * undefined, but callers deciding WHETHER to draw must ask `hasLevel` - these
 * numbers are a safe default, not evidence the level exists.
 */
export function dimensionsFor(levelid) {
  const level = levelFor(levelid === undefined ? state.currentlevelid : levelid)
  return {
    width: level?.mapwidth || FALLBACK_WIDTH,
    height: level?.mapheight || FALLBACK_HEIGHT,
  }
}

export function hasLevel(levelid) {
  return !!levelFor(levelid)
}

export function levelName(levelid) {
  const level = levelFor(levelid)
  if (!level) return null
  // Qualified by building only when there is more than one, so a single-building
  // site is not made to read "Main / Ground floor" everywhere.
  return state.buildings.length > 1
    ? `${level.buildingname} / ${level.levelname}`
    : level.levelname
}

// Every level flat, in building then level order, for a selector.
export function levelOptions() {
  const options = []
  state.buildings.forEach(building => {
    ;(building.levels || []).forEach(level => {
      options.push({
        levelid: level.levelid,
        levelname: level.levelname,
        buildingname: building.buildingname,
        label: state.buildings.length > 1
          ? `${building.buildingname} / ${level.levelname}`
          : level.levelname,
      })
    })
  })
  return options
}

export function useMapConfig() {
  loadMapConfig()
  return {
    state,
    blueprintUrlFor,
    dimensionsFor,
    hasLevel,
    levelName,
    levelOptions,
    setCurrentLevel,
  }
}
