<template>
  <div ref="wallRef" class="pantry-notes">
    <PageToolbar :title="strings.title">
      <template #actions>
        <NcActions :aria-label="strings.sortLabel" type="tertiary">
          <template #icon>
            <SortIcon :size="20" />
          </template>
          <NcActionButton
            v-for="opt in noteSortOptions"
            :key="opt.value"
            :class="{ 'pantry-sort-active': currentSort === opt.value }"
            @click="changeNoteSort(opt.value)"
          >
            <template #icon>
              <RadioboxMarkedIcon v-if="currentSort === opt.value" :size="20" />
              <RadioboxBlankIcon v-else :size="20" />
            </template>
            {{ opt.label }}
          </NcActionButton>
        </NcActions>
        <NcButton variant="primary" @click="openCreateDialog">
          <template #icon><PlusIcon :size="20" /></template>
          {{ strings.newNote }}
        </NcButton>
      </template>
    </PageToolbar>

    <div class="pantry-notes__body">
      <div v-if="loading" class="pantry-center">
        <NcLoadingIcon :size="36" />
      </div>

      <NcEmptyContent
        v-else-if="notes.length === 0"
        :name="strings.emptyTitle"
        :description="strings.emptyBody"
      >
        <template #icon>
          <NoteIcon />
        </template>
        <template #action>
          <NcButton variant="primary" @click="openCreateDialog">
            {{ strings.newNote }}
          </NcButton>
        </template>
      </NcEmptyContent>

      <template v-else>
        <!-- Selection bar -->
        <div v-if="selectedNoteIds.size > 0" class="pantry-selection-bar">
          <span>{{ selectionLabel }}</span>
          <NcButton variant="error" @click="confirmBulkDelete">
            <template #icon><DeleteIcon :size="20" /></template>
            {{ strings.delete }}
          </NcButton>
          <NcButton @click="selectedNoteIds.clear()">{{ strings.clearSelection }}</NcButton>
        </div>

        <div class="pantry-notes__grid">
          <template v-for="item in gridItems" :key="item.key">
            <div
              v-if="item.type === 'placeholder'"
              class="pantry-notes__placeholder"
              @dragover.prevent
              @drop.prevent.stop="onPlaceholderDrop"
            />
            <NoteCard
              v-else
              :note="item.note"
              :draggable-enabled="isCustomSort"
              :selected="selectedNoteIds.has(item.note.id)"
              @edit="openEditDialog"
              @delete="confirmDelete"
              @drag-start="onDragStart"
              @reorder-over="onReorderOver"
              @select="toggleNoteSelection"
            />
          </template>
        </div>
      </template>
    </div>

    <!-- Create/Edit dialog -->
    <NoteDialog
      v-if="showDialog"
      :open="showDialog"
      :note="editingNote"
      @update:open="closeDialog"
      @save="submitDialog"
    />

    <!-- Delete confirm -->
    <NcDialog
      v-if="deletingNote"
      :name="strings.deleteTitle"
      :open="!!deletingNote"
      close-on-click-outside
      @update:open="(v) => !v && (deletingNote = null)"
    >
      <p>{{ deleteBody }}</p>
      <template #actions>
        <NcButton @click="deletingNote = null">{{ strings.cancel }}</NcButton>
        <NcButton variant="error" @click="submitDelete">{{ strings.delete }}</NcButton>
      </template>
    </NcDialog>

    <!-- Bulk delete confirm -->
    <NcDialog
      v-if="bulkDeleting"
      :name="strings.deleteTitle"
      :open="bulkDeleting"
      close-on-click-outside
      @update:open="(v) => !v && (bulkDeleting = false)"
    >
      <p>{{ bulkDeleteBody }}</p>
      <template #actions>
        <NcButton @click="bulkDeleting = false">{{ strings.cancel }}</NcButton>
        <NcButton variant="error" @click="submitBulkDelete">{{ strings.delete }}</NcButton>
      </template>
    </NcDialog>
  </div>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { n, t } from '@nextcloud/l10n'
import NcButton from '@nextcloud/vue/components/NcButton'
import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'
import NcDialog from '@nextcloud/vue/components/NcDialog'
import NcActions from '@nextcloud/vue/components/NcActions'
import NcActionButton from '@nextcloud/vue/components/NcActionButton'
import PageToolbar from '@/components/PageToolbar'
import { NoteCard, NoteDialog } from '@/components/Notes'
import PlusIcon from '@icons/Plus.vue'
import NoteIcon from '@icons/Note.vue'
import DeleteIcon from '@icons/Delete.vue'
import SortIcon from '@icons/Sort.vue'
import RadioboxBlankIcon from '@icons/RadioboxBlank.vue'
import RadioboxMarkedIcon from '@icons/RadioboxMarked.vue'
import type { Note } from '@/api/types'
import type { NoteSort } from '@/api/prefs'
import { getNoteSort, setNoteSort } from '@/api/prefs'
import { useNotes } from '@/composables/useNotes'
import { useTouchReorder } from '@/composables/useTouchReorder'

const props = defineProps<{ houseId: string }>()

const houseIdNum = computed(() => Number(props.houseId))
const { notes, loading, load, create, update, remove, reorder, sortBy } = useNotes(houseIdNum.value)

// ----- Sort -----

const currentSort = ref<NoteSort>('custom')
const isCustomSort = computed(() => currentSort.value === 'custom')

const noteSortOptions: { value: NoteSort; label: string }[] = [
  { value: 'newest', label: t('pantry', 'Newest first') },
  { value: 'oldest', label: t('pantry', 'Oldest first') },
  { value: 'title_asc', label: t('pantry', 'Title A\u2013Z') },
  { value: 'title_desc', label: t('pantry', 'Title Z\u2013A') },
  { value: 'custom', label: t('pantry', 'Custom') },
]

async function loadSortPref() {
  const prefs = await getNoteSort(houseIdNum.value)
  currentSort.value = prefs.sort
  sortBy.value = prefs.sort
}

async function changeNoteSort(value: NoteSort) {
  currentSort.value = value
  sortBy.value = value
  await setNoteSort(houseIdNum.value, value)
  await load(value)
}

onMounted(async () => {
  await loadSortPref()
  await load()
})
watch(
  () => props.houseId,
  async () => {
    await loadSortPref()
    await load()
  },
)

// ----- Reorder -----

type GridItem = { type: 'note'; key: string; note: Note } | { type: 'placeholder'; key: string }

const draggingNoteId = ref<number | null>(null)
const dropIndex = ref<number | null>(null)
const wallRef = ref<HTMLElement | null>(null)

function buildGridItems(): GridItem[] {
  const dragId = draggingNoteId.value
  if (dragId === null || dropIndex.value === null) {
    return notes.value.map((n) => ({ type: 'note' as const, key: 'n-' + n.id, note: n }))
  }

  const without = notes.value.filter((n) => n.id !== dragId)
  const items: GridItem[] = without.map((n) => ({
    type: 'note' as const,
    key: 'n-' + n.id,
    note: n,
  }))
  const clampedIndex = Math.min(dropIndex.value, items.length)
  items.splice(clampedIndex, 0, { type: 'placeholder', key: 'drop-placeholder' })
  return items
}

const gridItems = computed(() => buildGridItems())

function onDragStart(noteId: number) {
  draggingNoteId.value = noteId
  dropIndex.value = null
}

function computeDropIndex(hoveredNoteId: number, clientX: number, target: HTMLElement | null) {
  const dragId = draggingNoteId.value
  if (!dragId || dragId === hoveredNoteId) return

  const without = notes.value.filter((n) => n.id !== dragId)
  const idx = without.findIndex((n) => n.id === hoveredNoteId)
  if (idx === -1) return

  if (target) {
    const rect = target.getBoundingClientRect()
    const past = clientX > rect.left + rect.width / 2
    dropIndex.value = past ? idx + 1 : idx
  } else {
    dropIndex.value = idx
  }
}

function onReorderOver(hoveredNoteId: number, e: MouseEvent) {
  computeDropIndex(hoveredNoteId, e.clientX, e.currentTarget as HTMLElement | null)
}

function onPlaceholderDrop() {
  commitReorder()
}

async function commitReorder() {
  const dragId = draggingNoteId.value
  const idx = dropIndex.value
  draggingNoteId.value = null
  dropIndex.value = null

  if (dragId === null || idx === null) return

  const dragged = notes.value.find((n) => n.id === dragId)
  if (!dragged) return

  const without = notes.value.filter((n) => n.id !== dragId)
  const clampedIndex = Math.min(idx, without.length)
  const reordered = [...without]
  reordered.splice(clampedIndex, 0, dragged)

  const items = reordered.map((n, i) => ({ id: n.id, sortOrder: i }))
  await reorder(items)
}

// Capture-phase listeners — commit the reorder on drop, reset on dragend.
function onDropCapture() {
  commitReorder()
}
function onDragEndCapture() {
  draggingNoteId.value = null
  dropIndex.value = null
}
onMounted(() => {
  wallRef.value?.addEventListener('drop', onDropCapture, true)
  wallRef.value?.addEventListener('dragend', onDragEndCapture, true)
})
onBeforeUnmount(() => {
  wallRef.value?.removeEventListener('drop', onDropCapture, true)
  wallRef.value?.removeEventListener('dragend', onDragEndCapture, true)
})

// ----- Touch reorder -----
useTouchReorder(
  wallRef,
  {
    onDragStart: onDragStart,
    onReorderOver(hoveredId, clientX) {
      const el = wallRef.value?.querySelector<HTMLElement>(`[data-drag-id="${hoveredId}"]`)
      computeDropIndex(hoveredId, clientX, el)
    },
    onDrop: commitReorder,
    onCancel() {
      draggingNoteId.value = null
      dropIndex.value = null
    },
  },
  isCustomSort,
)

// ----- Create / Edit -----

const showDialog = ref(false)
const editingNote = ref<Note | null>(null)

function openCreateDialog() {
  editingNote.value = null
  showDialog.value = true
}

function openEditDialog(note: Note) {
  editingNote.value = note
  showDialog.value = true
}

function closeDialog(v: boolean) {
  if (!v) {
    showDialog.value = false
    editingNote.value = null
  }
}

async function submitDialog(data: { title: string; content: string; color: string }) {
  if (editingNote.value) {
    await update(editingNote.value.id, {
      title: data.title,
      content: data.content,
      color: data.color,
    })
    // Update local ref so subsequent saves use the latest state
    editingNote.value = {
      ...editingNote.value,
      title: data.title,
      content: data.content || null,
      color: data.color || null,
    }
  } else {
    const created = await create(data.title, data.content || null, data.color || null)
    // Switch to editing the newly created note
    editingNote.value = created
  }
}

// ----- Selection -----

const selectedNoteIds = ref(new Set<number>())
const selectionLabel = computed(() =>
  n('pantry', '%n item selected', '%n items selected', selectedNoteIds.value.size),
)

function toggleNoteSelection(noteId: number) {
  const next = new Set(selectedNoteIds.value)
  if (next.has(noteId)) {
    next.delete(noteId)
  } else {
    next.add(noteId)
  }
  selectedNoteIds.value = next
}

const bulkDeleting = ref(false)

const bulkDeleteBody = computed(() =>
  n(
    'pantry',
    'Are you sure you want to delete %n note?',
    'Are you sure you want to delete %n notes?',
    selectedNoteIds.value.size,
  ),
)

function confirmBulkDelete() {
  bulkDeleting.value = true
}

async function submitBulkDelete() {
  const ids = [...selectedNoteIds.value]
  for (const id of ids) {
    await remove(id)
  }
  selectedNoteIds.value = new Set()
  bulkDeleting.value = false
}

// ----- Delete -----

const deletingNote = ref<Note | null>(null)
const deleteBody = computed(() =>
  t('pantry', 'Are you sure you want to delete "{name}"?', {
    name: deletingNote.value?.title ?? '',
  }),
)

function confirmDelete(note: Note) {
  deletingNote.value = note
}

async function submitDelete() {
  if (!deletingNote.value) return
  await remove(deletingNote.value.id)
  deletingNote.value = null
}

const strings = {
  title: t('pantry', 'Notes wall'),
  newNote: t('pantry', 'New note'),
  cancel: t('pantry', 'Cancel'),
  delete: t('pantry', 'Delete'),
  emptyTitle: t('pantry', 'No notes yet'),
  emptyBody: t('pantry', 'Create your first note to start sharing reminders with your household.'),
  deleteTitle: t('pantry', 'Delete note'),
  sortLabel: t('pantry', 'Sort order'),
  clearSelection: t('pantry', 'Clear selection'),
}
</script>

<style scoped lang="scss">
.pantry-notes {
  position: relative;
  min-height: 100%;

  &__body {
    max-width: 1100px;
    margin: 0 auto;
  }

  &__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 1rem;
    padding: 0 1rem 1rem;
  }

  &__placeholder {
    min-height: 80px;
    border: 3px dashed var(--color-primary-element);
    border-radius: var(--border-radius-large, 12px);
    background: rgba(var(--color-primary-element-rgb, 0, 120, 212), 0.08);
  }
}

.pantry-center {
  display: flex;
  justify-content: center;
  padding: 2rem;
}

.pantry-selection-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 1rem;
  margin: 0 1rem 0.75rem;
  background: var(--color-primary-element-light);
  border-radius: var(--border-radius-large, 12px);
  font-weight: 500;

  span:first-child {
    flex: 1;
  }
}

.pantry-sort-active {
  font-weight: 600;
}
</style>
