<template>
  <template v-if="enabled">
    <h4 class="printer-heading">Printers</h4>

    <div class="form-group">
      <label :for="`printersearch-${uid}`">Assigned printers</label>
      <input
        :id="`printersearch-${uid}`"
        v-model="search"
        type="text"
        class="form-control"
        placeholder="Filter printers..."
      />
      <div class="printer-list">
        <label v-for="option in filtered" :key="option.assetid" class="printer-item">
          <input
            type="checkbox"
            :checked="assigned.includes(option.assetid)"
            @change="toggle(option.assetid, $event.target.checked)"
          />
          <span>{{ label(option) }}</span>
          <span v-if="option.printer?.modelname" class="printer-meta">
            {{ option.printer.modelname }}
          </span>
        </label>
        <span v-if="!filtered.length" class="muted">No printers match.</span>
      </div>
      <small class="form-hint">{{ hint }}</small>
    </div>

    <div class="form-group">
      <label :for="`defaultprinter-${uid}`">Default printer</label>
      <select :id="`defaultprinter-${uid}`" v-model="defaultAssetId" class="form-control">
        <option :value="null">No default</option>
        <!-- Only what is assigned: a default the bay was never told to install
             fails to apply, and nothing in ShopDB shows why. -->
        <option v-for="option in assignedOptions" :key="option.assetid" :value="option.assetid">
          {{ label(option) }}
        </option>
      </select>
      <small class="form-hint">
        Optional. Applied per user at logon, because a default printer is a
        per-user setting that SYSTEM cannot set for somebody else.
      </small>
    </div>
  </template>
</template>

<script setup>
// One picker, used by the machine form and the PC form.
//
// The assignment belongs to the MACHINE - that is what makes a reimaged PC come
// back with the bay's printers - and the PC form writes the same shape as an
// override. Two copies of this UI would drift, and the two ends of an override
// disagreeing is exactly the bug nobody would spot.
import { ref, computed, watch, onMounted } from 'vue'
import { printersApi } from '@/api'

const props = defineProps({
  // The asset being edited. Null while creating: save(assetid) is called with
  // the new id once it exists.
  assetid: { type: Number, default: null },
  // Wording only. What it means to assign here differs: a machine's set is the
  // bay's, a PC's set overrides the machine it controls.
  scope: { type: String, default: 'machine' }
})

const uid = Math.random().toString(36).slice(2, 8)
const enabled = ref(false)
const printers = ref([])
const assigned = ref([])
const defaultAssetId = ref(null)
const search = ref('')

const filtered = computed(() => {
  const term = search.value.trim().toLowerCase()
  if (!term) return printers.value
  return printers.value.filter(option => label(option).toLowerCase().includes(term))
})

const assignedOptions = computed(() =>
  printers.value.filter(option => assigned.value.includes(option.assetid)))

const hint = computed(() => {
  const count = assigned.value.length
  if (props.scope === 'pc') {
    return `${count} assigned. Printers ticked here belong to this PC and REPLACE `
      + 'whatever the machine it controls is assigned - the whole set, not added to it.'
  }
  return `${count} assigned. These belong to the machine, so whichever PC controls `
    + 'it installs them - including a replacement PC after a reimage.'
})

function label(option) {
  // A real fleet has printers whose name is the literal string 'NONE' - an
  // import artefact - and showing that as the label makes two different
  // printers indistinguishable in the list.
  const name = option.name && option.name.toUpperCase() !== 'NONE' ? option.name : ''
  return name || option.assetnumber || `Printer ${option.assetid}`
}

function toggle(assetid, checked) {
  if (checked) {
    if (!assigned.value.includes(assetid)) assigned.value.push(assetid)
  } else {
    assigned.value = assigned.value.filter(id => id !== assetid)
    // Unassigning the default clears it rather than leaving a row pointing at a
    // printer the bay is no longer told to install.
    if (defaultAssetId.value === assetid) defaultAssetId.value = null
  }
}

async function loadOptions() {
  try {
    // listAll: perpage is clamped server-side, and a picker that stops at 100
    // silently hides printers sorting late in the alphabet.
    printers.value = await printersApi.listAll()
    enabled.value = true
  } catch (error) {
    // A site without the printers plugin has no section at all.
    enabled.value = false
  }
}

async function loadAssignment(assetid) {
  if (!enabled.value || !assetid) return
  try {
    const response = await printersApi.assignment.get(assetid)
    const data = response.data.data || {}
    assigned.value = data.printerassetids || []
    defaultAssetId.value = data.defaultprinterassetid ?? null
  } catch (error) {
    assigned.value = []
    defaultAssetId.value = null
  }
}

// Called by the parent AFTER the asset exists, so a new record can be assigned
// in the same save.
async function save(assetid) {
  if (!enabled.value || !assetid) return
  await printersApi.assignment.set(assetid, assigned.value, defaultAssetId.value)
}

defineExpose({ save })

onMounted(async () => {
  await loadOptions()
  await loadAssignment(props.assetid)
})

watch(() => props.assetid, assetid => loadAssignment(assetid))
</script>

<style scoped>
.printer-heading { margin-top: 1.5rem; margin-bottom: 1rem; }
.printer-list {
  max-height: 12rem;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.35rem 0.5rem;
  margin-top: 0.35rem;
}
.printer-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.15rem 0; }
.printer-meta { color: var(--text-light); font-size: 0.85em; }
.muted { color: var(--text-light); }
</style>
