<template>
  <div>
    <Button size="sm" variant="outline" class="h-8" @click="open = true">
      {{ crudLabel('customColumns') }}
    </Button>
    <Dialog :open="open" @update:open="onOpenChange">
      <DialogContent
        shell="crud"
        class="vm-col-custom__dialog"
        @pointer-down-outside="preventDialogOutsideClose"
        @focus-outside="preventDialogOutsideClose"
        @interact-outside="preventDialogOutsideClose"
      >
        <DialogHeader class="vm-crud-shell__head">
          <DialogTitle class="vm-crud-shell__title">{{ crudLabel('customColumns') }}</DialogTitle>
        </DialogHeader>
        <div class="vm-crud-shell__body">
          <div class="vm-col-custom">
            <label
              v-for="(item, i) in list"
              :key="String(item.prop)"
              class="vm-col-custom__item"
            >
              <input v-model="item.checked" type="checkbox" />
              <span>{{ item.label }}</span>
              <span class="vm-col-custom__move">
                <button type="button" :disabled="i === 0" @click.prevent="move(i, -1)">↑</button>
                <button
                  type="button"
                  :disabled="i === list.length - 1"
                  @click.prevent="move(i, 1)"
                >
                  ↓
                </button>
              </span>
            </label>
          </div>
        </div>
        <DialogFooter class="vm-crud-shell__foot">
          <button type="button" class="vm-crud-shell__btn is-ghost" @click="open = false">
            {{ crudLabel('close') }}
          </button>
          <button type="button" class="vm-crud-shell__btn is-danger" @click="reset">
            {{ crudLabel('reset') }}
          </button>
          <button type="button" class="vm-crud-shell__btn is-primary" @click="save">
            {{ crudLabel('save') }}
          </button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  </div>
</template>

<script setup lang="ts">
import { crudLabel } from '../style'
import { ref, watch, inject } from 'vue'
import { preventDialogOutsideClose } from '../../lib/dialog-float'
import { injectTableOptions, TABLE_API_KEY } from '../key'
import type { CrudColumn } from '../../../../typings/admin/comm/crud'
defineOptions({ name: 'vm-column-custom' })

const props = defineProps<{
  name?: string
  columns?: CrudColumn[]
}>()

const emit = defineEmits<{
  change: [cols: CrudColumn[]]
  'update:modelValue': [cols: CrudColumn[]]
}>()

const tableApi = inject(TABLE_API_KEY, null)
const tableOpts = injectTableOptions()

type Item = { label: string; prop: string; checked: boolean }

const open = ref(false)
const list = ref<Item[]>([])
const storageKey = computedKey()

function onOpenChange(v: boolean) {
  open.value = v
}

function computedKey() {
  return `column-custom__${props.name || (typeof location !== 'undefined' ? location.pathname : 'default')}`
}

function sourceColumns(): CrudColumn[] {
  if (props.columns?.length) return props.columns
  return tableApi?.getColumns() || tableOpts?.columns || []
}

function buildList() {
  const saved = load()
  list.value = sourceColumns()
    .filter((c) => c.prop && c.type !== 'selection' && c.type !== 'op')
    .map((c) => ({
      label: c.label || String(c.prop),
      prop: String(c.prop),
      checked: saved?.[c.prop!]?.checked ?? !c.hidden,
    }))
}

function load(): Record<string, { checked: boolean; order?: number }> | null {
  try {
    return JSON.parse(localStorage.getItem(storageKey) || 'null')
  } catch {
    return null
  }
}

watch(
  () => [props.columns, tableOpts?.columns, open.value] as const,
  () => buildList(),
  { immediate: true, deep: true },
)

function move(i: number, delta: number) {
  const j = i + delta
  if (j < 0 || j >= list.value.length) return
  const arr = [...list.value]
  const t = arr[i]!
  arr[i] = arr[j]!
  arr[j] = t
  list.value = arr
}

function reset() {
  localStorage.removeItem(storageKey)
  buildList()
}

function save() {
  const map: Record<string, { checked: boolean; order: number }> = {}
  list.value.forEach((it, order) => {
    map[it.prop] = { checked: it.checked, order }
  })
  localStorage.setItem(storageKey, JSON.stringify(map))
  const cols = sourceColumns()
  const ordered = [...cols].sort((a, b) => {
    const ao = a.prop != null ? map[a.prop]?.order ?? 999 : 999
    const bo = b.prop != null ? map[b.prop]?.order ?? 999 : 999
    return ao - bo
  })
  const next = ordered.map((c) => {
    if (!c.prop || c.type === 'selection' || c.type === 'op') return c
    return { ...c, hidden: !(map[c.prop]?.checked ?? true) }
  })
  tableApi?.setColumns(next)
  emit('change', next)
  emit('update:modelValue', next)
  open.value = false
}
</script>

<style lang="scss" scoped>
.vm-col-custom {
  display: grid;
  gap: 8px;
}

.vm-col-custom__item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 13px;
  cursor: pointer;
}

.vm-col-custom__move {
  margin-left: auto;
  display: flex;
  gap: 4px;

  button {
    width: 24px;
    height: 24px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--card);
    cursor: pointer;

    &:disabled {
      opacity: 0.4;
    }
  }
}
</style>
