<template>
  <div class="weekgrid" @pointerleave="dragging = false">
    <button
      v-for="(d, di) in displayLabels"
      :key="d + di"
      type="button"
      class="day"
      :class="{
        on: anySelected && displayDisplayValues[di],
        // Show fallback styling on the reordered fallback day when none selected
        fallback: !anySelected && displayDefault[di],
        pressing: isPressing(di),
        preview: previewActive && inPreviewRange(di),
      }"
      @pointerdown="onPointerDown(di)"
      @pointerenter="onDragOver(di)"
      @pointerup="onPointerUp"
    >
      {{ d.slice(0, 3) }}
    </button>
    <button
      v-for="g in barGroups"
      :key="g.start"
      type="button"
      tabindex="-1"
      class="workday-weekend"
      :style="{ gridColumn: 'span ' + g.span }"
      @click.stop="toggleWeekend(g.type)"
    >
      <div :class="{ workday: !g.type, weekend: g.type }"></div>
    </button>
  </div>
</template>

<script setup>
import { computed, ref } from 'vue'
import {
  getLocalizedWeekdayNames,
  getLocaleFirstDay,
  getLocaleWeekendDays,
  reorderByFirstDay,
} from '@/utils/date'

const model = defineModel({
  type: Array,
  default: () => [false, false, false, false, false, false, false],
})

const props = defineProps({
  weekend: { type: Array, default: undefined },
  fallback: {
    type: Array,
    default: () => [false, false, false, false, false, false, false],
  },
  firstDay: { type: Number, default: null },
})

// If external model provided is entirely false, keep as-is (user will see fallback styling),
// only overwrite if null/undefined.
if (!model.value) model.value = [...props.fallback]
const labelsMondayFirst = getLocalizedWeekdayNames()
const labels = [labelsMondayFirst[6], ...labelsMondayFirst.slice(0, 6)]
const anySelected = computed(() => model.value.some(Boolean))
const localeFirst = getLocaleFirstDay()
const localeWeekend = getLocaleWeekendDays()
const firstDay = computed(() => (props.firstDay ?? localeFirst) % 7)

const weekendDays = computed(() => {
  if (props.weekend && props.weekend.length === 7) return props.weekend
  return localeWeekend
})

const displayLabels = computed(() => reorderByFirstDay(labels, firstDay.value))
const displayValuesCommitted = computed(() => reorderByFirstDay(model.value, firstDay.value))
const displayWorking = computed(() => reorderByFirstDay(weekendDays.value, firstDay.value))
const displayDefault = computed(() => reorderByFirstDay(props.fallback, firstDay.value))

// Mapping from display index to original model index
const orderIndices = computed(() => Array.from({ length: 7 }, (_, i) => (i + firstDay.value) % 7))

const barGroups = computed(() => {
  const arr = displayWorking.value
  const groups = []
  let type = arr[0]
  let start = 0
  for (let i = 1; i <= arr.length; i++) {
    if (i === arr.length || arr[i] !== type) {
      groups.push({ type, start, span: i - start })
      if (i < arr.length) {
        type = arr[i]
        start = i
      }
    }
  }
  return groups
})

const dragging = ref(false)
const previewActive = ref(false)
const dragVal = ref(false)
const dragStart = ref(null)
const previewEnd = ref(null)
let originalValues = null

// Preview (drag) values; when none selected, still return committed (not fallback) so 'on' class
// is suppressed and only fallback styling applies via displayDefault
const displayPreviewValues = computed(() => {
  if (
    !dragging.value ||
    !previewActive.value ||
    dragStart.value == null ||
    previewEnd.value == null ||
    !originalValues
  ) {
    return displayValuesCommitted.value
  }
  const [s, e] =
    dragStart.value < previewEnd.value
      ? [dragStart.value, previewEnd.value]
      : [previewEnd.value, dragStart.value]
  return displayValuesCommitted.value.map((v, di) => (di >= s && di <= e ? dragVal.value : v))
})
const displayDisplayValues = displayPreviewValues

function inPreviewRange(di) {
  if (!previewActive.value || dragStart.value == null || previewEnd.value == null) return false
  const [s, e] =
    dragStart.value < previewEnd.value
      ? [dragStart.value, previewEnd.value]
      : [previewEnd.value, dragStart.value]
  return di >= s && di <= e
}
function isPressing(di) {
  return dragging.value && !previewActive.value && dragStart.value === di
}

function onPointerDown(di) {
  originalValues = [...model.value]
  dragVal.value = !model.value[(di + firstDay.value) % 7]
  dragStart.value = di
  previewEnd.value = di
  dragging.value = true
  previewActive.value = false
  window.addEventListener('pointerup', onPointerUp, { once: true })
}
function onDragOver(di) {
  if (!dragging.value) return
  if (previewEnd.value === di) return
  if (!previewActive.value && di !== dragStart.value) previewActive.value = true
  previewEnd.value = di
}
function onPointerUp() {
  if (!dragging.value) return
  if (!previewActive.value) {
    // simple click: toggle single
    const next = [...originalValues]
    next[(dragStart.value + firstDay.value) % 7] = dragVal.value
    model.value = next
    cleanupDrag()
  } else {
    commitDrag()
  }
}
function commitDrag() {
  if (dragStart.value == null || previewEnd.value == null || !originalValues) return cancelDrag()
  const [s, e] =
    dragStart.value < previewEnd.value
      ? [dragStart.value, previewEnd.value]
      : [previewEnd.value, dragStart.value]
  const next = [...originalValues]
  for (let di = s; di <= e; di++) next[orderIndices.value[di]] = dragVal.value
  model.value = next
  cleanupDrag()
}
function cancelDrag() {
  cleanupDrag()
}
function cleanupDrag() {
  dragging.value = false
  previewActive.value = false
  dragStart.value = null
  previewEnd.value = null
  originalValues = null
}
function toggleWeekend(work) {
  const base = weekendDays.value
  const target = work ? base : base.map((v) => !v)
  const current = model.value
  const allOn = current.every(Boolean)
  const isTargetActive = current.every((v, i) => v === target[i])
  if (allOn || isTargetActive) {
    model.value = [false, false, false, false, false, false, false]
  } else {
    model.value = [...target]
  }
}
</script>

<style scoped>
.weekgrid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-auto-rows: auto;
}
.workday-weekend {
  height: 1em;
  border: 0;
  background: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}
.workday-weekend div {
  height: 0.3em;
  border-radius: 0.15em;
  margin: 0.1em;
}
.workday {
  background: var(--workday, #888);
}
.weekend {
  background: var(--weekend, #f88);
}
.day {
  flex: 1;
  cursor: pointer;
  background: var(--panel-alt);
  color: var(--ink);
  font-size: 0.65rem;
  font-weight: 500;
  padding: 0.55rem 0.35rem;
  border: none;
  margin: 0 1px;
  border-radius: 0.4rem;
  user-select: none;
}
.day.on {
  background: var(--pill-active-bg);
  color: var(--pill-active-ink);
  font-weight: 600;
}
.day.pressing {
  filter: brightness(1.15);
}
.day.preview {
  filter: brightness(1.15);
}
.day.fallback {
  background: var(--muted-alt);
  opacity: 0.65;
}
</style>
