<script setup>
import deLocale from "@fullcalendar/core/locales/de"
import FullCalendar from "@fullcalendar/vue3"
import interactionPlugin from "@fullcalendar/interaction"
import resourceTimelinePlugin from "@fullcalendar/resource-timeline"
import { parseDate } from "@internationalized/date"

const router = useRouter()
const auth = useAuthStore()
const profileStore = useProfileStore()
const toast = useToast()
const { $api, $dayjs } = useNuxtApp()
const { create: createEvent } = useEntities("events")
const { list: listStaffTimeSpans, createEntry, update: updateStaffTimeEntry } = useStaffTime()

const loading = ref(true)
const savingAbsence = ref(false)
const selectedType = ref("all")
const calendarRef = ref(null)
const calendarView = ref("resourceTimelineWeek")
const calendarCurrentDate = ref($dayjs().format("YYYY-MM-DD"))
const calendarTitle = ref("")
const visibleRange = ref({
  from: $dayjs().startOf("month").format("YYYY-MM-DD"),
  to: $dayjs().endOf("month").format("YYYY-MM-DD")
})
const lastRangeKey = ref("")
const resources = ref([])
const events = ref([])
const profiles = ref([])
const inventoryitems = ref([])
const savingQuickConfig = ref(false)
const isQuickConfigModalOpen = ref(false)
const showQuickConfigEditor = ref(true)
const showQuickPresetManagement = ref(false)
const quickEntryConfig = reactive({
  name: "Quick-Eintrag",
  color: "#2563eb"
})
const newQuickPresetName = ref("")
const quickEntryColorOptions = [
  { label: "Blau", value: "#2563eb" },
  { label: "Grün", value: "#16a34a" },
  { label: "Orange", value: "#ea580c" },
  { label: "Rot", value: "#dc2626" },
  { label: "Pink", value: "#db2777" },
  { label: "Türkis", value: "#0f766e" },
  { label: "Grau", value: "#4b5563" },
  { label: "Schwarz", value: "#111827" }
]

const isAbsenceModalOpen = ref(false)
const isProfileDetailsModalOpen = ref(false)
const loadingProfileVacation = ref(false)
const selectedProfile = ref(null)
const selectedProfileVacationSummary = ref(null)
const profileVacationRequestId = ref(0)
const absenceForm = reactive({
  mode: "create",
  entry: null,
  userId: "",
  type: "vacation",
  startDate: $dayjs().format("YYYY-MM-DD"),
  endDate: $dayjs().format("YYYY-MM-DD"),
  description: ""
})

const setAbsenceDateToToday = (field) => {
  absenceForm[field] = $dayjs().format("YYYY-MM-DD")
}

const startDateValue = computed({
  get: () => {
    if (!absenceForm.startDate) return null

    try {
      return parseDate(absenceForm.startDate)
    } catch {
      return null
    }
  },
  set: (value) => {
    absenceForm.startDate = value ? value.toString() : ""
  }
})

const endDateValue = computed({
  get: () => {
    if (!absenceForm.endDate) return null

    try {
      return parseDate(absenceForm.endDate)
    } catch {
      return null
    }
  },
  set: (value) => {
    absenceForm.endDate = value ? value.toString() : ""
  }
})

const resourceTypeOptions = [
  { label: "Alle Ressourcen", value: "all" },
  { label: "Teams", value: "Team" },
  { label: "Profile", value: "Profile" },
  { label: "Inventarartikel", value: "Inventarartikel" }
]

const calendarViewOptions = [
  { label: "Tag", value: "resourceTimelineDay" },
  { label: "Woche", value: "resourceTimelineWeek" },
  { label: "Monat", value: "resourceTimelineMonth" }
]

const absenceTypeOptions = [
  { label: "Urlaub", value: "vacation" },
  { label: "Krank", value: "sick" }
]

const calendarPickerValue = computed({
  get: () => {
    if (!calendarCurrentDate.value) return null

    try {
      return parseDate(calendarCurrentDate.value)
    } catch {
      return null
    }
  },
  set: (value) => {
    calendarCurrentDate.value = value ? value.toString() : ""
    if (value) {
      const api = calendarRef.value?.getApi?.()
      api?.gotoDate(value.toString())
    }
  }
})

const profileOptions = computed(() =>
  profiles.value
    .filter((profile) => !profile.archived && profile.user_id)
    .map((profile) => ({
      label: profile.full_name || profile.fullName || [profile.first_name, profile.last_name].filter(Boolean).join(" ") || profile.email,
      value: profile.user_id
    }))
)

const absenceModalTitle = computed(() =>
  absenceForm.mode === "edit"
    ? (absenceForm.type === "sick" ? "Krankmeldung bearbeiten" : "Urlaub bearbeiten")
    : (absenceForm.type === "sick" ? "Krank eintragen" : "Urlaub eintragen")
)

const tenantCalendarConfig = computed(() =>
  auth.activeTenantData?.calendarConfig
  || profileStore.ownTenant?.calendarConfig
  || {}
)

const resolvedQuickEntryConfig = computed(() => {
  const config = tenantCalendarConfig.value?.quickEntry || {}

  return {
    name: config.name || "Quick-Eintrag",
    color: config.color || "#2563eb"
  }
})

const activeQuickEntryConfig = computed(() => ({
  name: quickEntryConfig.name?.trim() || resolvedQuickEntryConfig.value.name,
  color: quickEntryConfig.color || resolvedQuickEntryConfig.value.color
}))

const quickEntryPresets = computed(() => {
  const rawPresets = tenantCalendarConfig.value?.quickEntryPresets

  if (!Array.isArray(rawPresets)) return []

  return rawPresets
    .filter((preset) => preset?.name && preset?.color)
    .map((preset) => ({
      name: String(preset.name),
      color: String(preset.color)
    }))
})

const resourcesById = computed(() => new Map(resources.value.map((resource) => [resource.id, resource])))
const currentVacationYear = computed(() => $dayjs().year())
const selectedProfileTeamsLabel = computed(() =>
  (selectedProfile.value?.teams || [])
    .map((team) => team?.name)
    .filter(Boolean)
    .join(", ") || "Keine Teams zugeordnet"
)
const selectedProfileBranchesLabel = computed(() => {
  const profile = selectedProfile.value
  if (!profile) return "Keine Niederlassung"

  const labels = [
    profile.branch?.name,
    ...(profile.branches || []).map((branch) => branch?.name)
  ].filter(Boolean)

  return [...new Set(labels)].join(", ") || "Keine Niederlassung"
})
const selectedProfileVacationDaysTaken = computed(() =>
  Number(selectedProfileVacationSummary.value?.sumVacationDays || 0)
)
const selectedProfileAnnualLeaveDays = computed(() => {
  const value = selectedProfile.value?.annual_paid_leave_days
  return value === null || value === undefined || value === "" ? null : Number(value)
})
const selectedProfileRemainingVacationDays = computed(() => {
  if (selectedProfileAnnualLeaveDays.value === null) return null

  return Number((selectedProfileAnnualLeaveDays.value - selectedProfileVacationDaysTaken.value).toFixed(2))
})

const visibleResources = computed(() => {
  if (selectedType.value === "all") return resources.value

  const visibleMap = new Map()

  resources.value
    .filter((resource) => resource.type === selectedType.value)
    .forEach((resource) => {
      let current = resource

      while (current) {
        visibleMap.set(current.id, current)
        current = current.parentId ? resourcesById.value.get(current.parentId) : null
      }
    })

  return resources.value.filter((resource) => visibleMap.has(resource.id))
})

const visibleResourceIds = computed(() => new Set(visibleResources.value.map((resource) => resource.id)))

const visibleEvents = computed(() => {
  if (selectedType.value === "all") return events.value

  return events.value.filter((event) =>
    (event.resourceIds || []).some((resourceId) => visibleResourceIds.value.has(resourceId))
  )
})

const calendarOptions = computed(() => ({
  schedulerLicenseKey: "CC-Attribution-NonCommercial-NoDerivatives",
  locale: deLocale,
  plugins: [resourceTimelinePlugin, interactionPlugin],
  initialView: calendarView.value,
  initialDate: calendarCurrentDate.value,
  headerToolbar: false,
  resourceAreaWidth: "280px",
  resourceGroupField: "type",
  resourceOrder: "type,title",
  resources: visibleResources.value,
  events: visibleEvents.value,
  nowIndicator: true,
  selectable: true,
  height: "calc(100vh - 250px)",
  slotMinWidth: 80,
  resourceAreaColumns: [
    {
      field: "title",
      headerContent: "Ressource"
    }
  ],
  resourceLabelDidMount(info) {
    const { resource } = info
    const isProfileResource = resource.extendedProps?.resourceKind === "profile" && resource.extendedProps?.profileId

    if (!isProfileResource) return

    info.el.classList.add("cursor-pointer")
    info.el.setAttribute("role", "button")
    info.el.setAttribute("tabindex", "0")
    info.el.setAttribute("title", "Mitarbeiterdetails öffnen")

    const openProfile = () => openProfileDetails(resource.extendedProps.profileId)
    const onKeydown = (event) => {
      if (event.key === "Enter" || event.key === " ") {
        event.preventDefault()
        openProfile()
      }
    }

    info.el.addEventListener("click", openProfile)
    info.el.addEventListener("keydown", onKeydown)
  },
  views: {
    resourceTimelineDay: {
      type: "resourceTimeline",
      duration: { days: 1 },
      buttonText: "Tag",
      slotDuration: { hours: 1 }
    },
    resourceTimelineWeek: {
      type: "resourceTimeline",
      duration: { days: 7 },
      buttonText: "Woche",
      slotDuration: { hours: 3 },
      weekends: false,
      slotMinTime: "06:00:00",
      slotMaxTime: "21:00:00"
    },
    resourceTimelineMonth: {
      type: "resourceTimeline",
      duration: { months: 1 },
      buttonText: "Monat"
    }
  },
  select(info) {
    createQuickEvent(info)
  },
  eventClick(info) {
    if (info.event.extendedProps.entrytype === "staff-absence") {
      openAbsenceModal(info.event.extendedProps.absenceType, {
        entry: info.event.extendedProps.absenceEntry,
        userId: info.event.extendedProps.absenceEntry?.user_id || "",
        startDate: $dayjs(info.event.extendedProps.absenceEntry?.started_at).format("YYYY-MM-DD"),
        endDate: $dayjs(info.event.extendedProps.absenceEntry?.stopped_at || info.event.extendedProps.absenceEntry?.started_at).format("YYYY-MM-DD"),
        description: info.event.extendedProps.absenceEntry?.description || ""
      })
      return
    }

    router.push(`/standardEntity/events/edit/${info.event.extendedProps.eventId}`)
  },
  datesSet(info) {
    const nextFrom = $dayjs(info.start).format("YYYY-MM-DD")
    const nextTo = $dayjs(info.end).subtract(1, "day").format("YYYY-MM-DD")
    const nextKey = `${nextFrom}:${nextTo}`

    calendarView.value = info.view.type
    calendarCurrentDate.value = $dayjs(info.view.currentStart).format("YYYY-MM-DD")
    calendarTitle.value = info.view.title

    if (nextKey === lastRangeKey.value) return

    lastRangeKey.value = nextKey
    visibleRange.value = { from: nextFrom, to: nextTo }
    loadPlanningBoard()
  }
}))

function resolveEventColor(eventType) {
  return tenantCalendarConfig.value?.eventTypes?.find((item) => item.label === eventType)?.color || "#111827"
}

function resolveEventTitle(event, projectsById) {
  if (event.name) return event.name
  if (event.project && projectsById.has(event.project)) return projectsById.get(event.project).name
  return event.quick ? activeQuickEntryConfig.value.name : "Planung"
}

function resolveRenderedEventColor(event) {
  if (event?.quick) return activeQuickEntryConfig.value.color
  return resolveEventColor(event.eventtype)
}

function getProfileLabel(profile) {
  return profile?.full_name || profile?.fullName || [profile?.first_name, profile?.last_name].filter(Boolean).join(" ") || profile?.email || `Profil ${profile?.id || ""}`.trim()
}

function getAbsenceTitle(entry) {
  const baseLabel = entry.type === "sick" ? "Krank" : "Urlaub"
  return entry.description ? `${baseLabel}: ${entry.description}` : baseLabel
}

function getAbsenceColor(type) {
  return type === "sick" ? "#dc2626" : "#d97706"
}

function getTeamLabel(team) {
  if (!team) return "Team"
  return team.name
}

function getBranchResourceId(branchId) {
  return `B-${branchId}`
}

function getProfileResourceIds(profile) {
  const assignedTeams = Array.isArray(profile?.teams) ? profile.teams.filter((team) => team?.id) : []

  if (!assignedTeams.length) {
    return [`P-${profile.id}`]
  }

  return assignedTeams.map((team) => `T-${team.id}:P-${profile.id}`)
}

function normalizeSelectedResourceIds(resourceId) {
  if (!resourceId) return []

  if (resourceId.startsWith("T-") && resourceId.includes(":P-")) {
    return [`P-${resourceId.split(":P-")[1]}`]
  }

  if (resourceId.startsWith("T-")) {
    const teamId = Number(resourceId.replace("T-", ""))
    return profiles.value
      .filter((profile) => (profile.teams || []).some((team) => team?.id === teamId))
      .map((profile) => `P-${profile.id}`)
  }

  return [resourceId]
}

function buildResources({ profiles, inventoryitems }) {
  const branchResources = []
  const teamResources = []
  const profileResources = []
  const topLevelTeamGroupId = "TEAM-UNBOUND"

  profiles
    .filter((profile) => !profile.archived)
    .forEach((profile) => {
      const assignedTeams = Array.isArray(profile?.teams) ? profile.teams.filter((team) => team?.id) : []

      if (!assignedTeams.length) {
        profileResources.push({
          id: `P-${profile.id}`,
          type: "Profile",
          title: getProfileLabel(profile),
          resourceKind: "profile",
          profileId: profile.id
        })
        return
      }

      assignedTeams.forEach((team) => {
        if (team?.branch?.id) {
          const branchResourceId = getBranchResourceId(team.branch.id)

          if (!branchResources.find((resource) => resource.id === branchResourceId)) {
            branchResources.push({
              id: branchResourceId,
              type: "Niederlassung",
              title: team.branch.name
            })
          }
        }

        if (!team?.branch?.id && !branchResources.find((resource) => resource.id === topLevelTeamGroupId)) {
          branchResources.push({
            id: topLevelTeamGroupId,
            type: "Niederlassung",
            title: "Übergreifende Teams"
          })
        }

        if (!teamResources.find((resource) => resource.id === `T-${team.id}`)) {
          teamResources.push({
            id: `T-${team.id}`,
            type: "Team",
            title: getTeamLabel(team),
            parentId: team?.branch?.id ? getBranchResourceId(team.branch.id) : topLevelTeamGroupId
          })
        }

        profileResources.push({
          id: `T-${team.id}:P-${profile.id}`,
          parentId: `T-${team.id}`,
          type: "Profile",
          title: getProfileLabel(profile),
          resourceKind: "profile",
          profileId: profile.id
        })
      })
    })

  const inventoryResources = inventoryitems
    .filter((item) => !item.archived && item.usePlanning)
    .map((item) => ({
      id: `I-${item.id}`,
      type: "Inventarartikel",
      title: item.name
    }))

  return [...branchResources, ...teamResources, ...profileResources, ...inventoryResources]
}

function buildEvents({ rawEvents, projectsById }) {
  const mappedEvents = rawEvents
    .filter((event) => !event.archived)
    .map((event) => {
      const resourceIds = [
        ...(profiles.value
          .filter((profile) => (event.profiles || []).includes(profile.id))
          .flatMap((profile) => getProfileResourceIds(profile))),
        ...(event.inventoryitems || []).map((itemId) => `I-${itemId}`)
      ]

      return {
        title: resolveEventTitle(event, projectsById),
        start: event.startDate,
        end: event.endDate,
        resourceIds,
        backgroundColor: resolveRenderedEventColor(event),
        borderColor: resolveRenderedEventColor(event),
        textColor: "#ffffff",
        entrytype: "event",
        eventId: event.id
      }
    })
    .filter((event) => event.resourceIds.length > 0)

  return mappedEvents
}

function buildAbsenceEvents(spansByUserId) {
  const profileByUserId = new Map(
    profiles.value
      .filter((profile) => profile.user_id)
      .map((profile) => [profile.user_id, profile])
  )

  return spansByUserId.flatMap(({ userId, spans }) => {
    const profile = profileByUserId.get(userId)
    if (!profile) return []

    return (spans || [])
      .filter((entry) => ["vacation", "sick"].includes(entry.type) && entry.state !== "rejected")
      .map((entry) => ({
        title: getAbsenceTitle(entry),
        start: $dayjs(entry.started_at).startOf("day").toISOString(),
        end: $dayjs(entry.stopped_at || entry.started_at).add(1, "day").startOf("day").toISOString(),
        resourceIds: getProfileResourceIds(profile),
        allDay: true,
        backgroundColor: getAbsenceColor(entry.type),
        borderColor: getAbsenceColor(entry.type),
        textColor: "#ffffff",
        entrytype: "staff-absence",
        absenceType: entry.type,
        absenceEntry: entry
      }))
  })
}

function resetAbsenceForm() {
  absenceForm.mode = "create"
  absenceForm.entry = null
  absenceForm.userId = profileOptions.value[0]?.value || ""
  absenceForm.type = "vacation"
  absenceForm.startDate = $dayjs().format("YYYY-MM-DD")
  absenceForm.endDate = $dayjs().format("YYYY-MM-DD")
  absenceForm.description = ""
}

function getCalendarApi() {
  return calendarRef.value?.getApi?.()
}

function changeCalendarView(view) {
  const api = getCalendarApi()
  if (!api || !view) return
  calendarView.value = view
  api.changeView(view)
}

function moveCalendar(direction) {
  const api = getCalendarApi()
  if (!api) return

  if (direction === "prev") api.prev()
  if (direction === "next") api.next()
}

function moveCalendarToday() {
  const api = getCalendarApi()
  if (!api) return
  api.today()
}

function formatProfileDate(value) {
  if (!value) return "Nicht hinterlegt"
  return $dayjs(value).isValid() ? $dayjs(value).format("DD.MM.YYYY") : "Nicht hinterlegt"
}

function formatProfileNumber(value) {
  if (value === null || value === undefined || value === "") return "Nicht hinterlegt"

  const numericValue = Number(value)
  return Number.isFinite(numericValue)
    ? new Intl.NumberFormat("de-DE", { minimumFractionDigits: 0, maximumFractionDigits: 2 }).format(numericValue)
    : String(value)
}

async function openProfileDetails(profileId) {
  const profile = profiles.value.find((item) => item.id === profileId)
  if (!profile) return

  selectedProfile.value = profile
  selectedProfileVacationSummary.value = null
  isProfileDetailsModalOpen.value = true

  const requestId = profileVacationRequestId.value + 1
  profileVacationRequestId.value = requestId

  if (!profile.user_id) {
    loadingProfileVacation.value = false
    return
  }

  loadingProfileVacation.value = true

  try {
    const from = `${currentVacationYear.value}-01-01`
    const to = `${currentVacationYear.value}-12-31`
    const response = await $api(`/api/staff/time/evaluation?${new URLSearchParams({
      from,
      to,
      targetUserId: profile.user_id
    }).toString()}`)

    if (profileVacationRequestId.value !== requestId) return

    selectedProfileVacationSummary.value = response?.summary || null
  } catch (error) {
    console.error("openProfileDetails failed", error)

    if (profileVacationRequestId.value !== requestId) return

    selectedProfileVacationSummary.value = null
    toast.add({
      title: "Resturlaub konnte nicht geladen werden",
      description: "Die Mitarbeiterdetails sind sichtbar, aber die Urlaubsauswertung konnte nicht abgerufen werden.",
      color: "orange"
    })
  } finally {
    if (profileVacationRequestId.value === requestId) {
      loadingProfileVacation.value = false
    }
  }
}

function openQuickConfig() {
  newQuickPresetName.value = quickEntryConfig.name
  showQuickConfigEditor.value = true
  showQuickPresetManagement.value = false
  isQuickConfigModalOpen.value = true
}

function applyQuickPreset(preset) {
  if (!preset) return
  quickEntryConfig.name = preset.name
  quickEntryConfig.color = preset.color
  newQuickPresetName.value = preset.name
}

watch(resolvedQuickEntryConfig, (config) => {
  if (!quickEntryConfig.name?.trim()) quickEntryConfig.name = config.name
  if (!quickEntryConfig.color) quickEntryConfig.color = config.color
}, { immediate: true })

async function persistQuickPresets(nextPresets) {
  if (savingQuickConfig.value) return

  savingQuickConfig.value = true

  try {
    const currentTenantData = auth.activeTenantData || {}
    const nextCalendarConfig = {
      ...(currentTenantData.calendarConfig || {}),
      quickEntryPresets: nextPresets
    }

    const updatedTenant = await $api(`/api/tenant/other/${auth.activeTenant}`, {
      method: "PUT",
      body: {
        data: {
          calendarConfig: nextCalendarConfig
        }
      }
    })

    auth.activeTenantData = updatedTenant
    profileStore.ownTenant = updatedTenant
  } catch (error) {
    console.error("persistQuickPresets failed", error)
    toast.add({
      title: "Vorlagen konnten nicht gespeichert werden",
      description: error?.message || "Bitte erneut versuchen.",
      color: "red"
    })
    throw error
  } finally {
    savingQuickConfig.value = false
  }
}

async function saveQuickPreset() {
  const presetName = newQuickPresetName.value?.trim() || quickEntryConfig.name?.trim()
  if (!presetName) {
    toast.add({ title: "Name fehlt", description: "Bitte einen Namen für die Vorlage angeben.", color: "orange" })
    return
  }

  const nextPresets = [
    ...quickEntryPresets.value.filter((preset) => preset.name !== presetName),
    {
      name: presetName,
      color: quickEntryConfig.color || "#2563eb"
    }
  ]

  await persistQuickPresets(nextPresets)
  toast.add({ title: "Vorlage gespeichert", color: "green" })
}

async function deleteQuickPreset(presetName) {
  const nextPresets = quickEntryPresets.value.filter((preset) => preset.name !== presetName)
  await persistQuickPresets(nextPresets)
  toast.add({ title: "Vorlage gelöscht", color: "green" })
}

async function createQuickEvent(info) {
  const resourceIds = normalizeSelectedResourceIds(info.resource?.id)

  const payload = {
    name: activeQuickEntryConfig.value.name,
    quick: true,
    startDate: info.startStr,
    endDate: info.endStr,
    profiles: resourceIds
      .filter((resourceId) => resourceId.startsWith("P-"))
      .map((resourceId) => resourceId.replace("P-", "")),
    inventoryitems: resourceIds
      .filter((resourceId) => resourceId.startsWith("I-"))
      .map((resourceId) => Number(resourceId.replace("I-", ""))),
    inventoryitemgroups: [],
    vehicles: [],
    notes: "",
    link: "",
    project: null,
    customer: null,
    vendor: null,
  }

  try {
    await createEvent(payload, true)
    toast.add({ title: "Quick-Eintrag angelegt", color: "green" })
    await loadPlanningBoard()
  } catch (error) {
    console.error("createQuickEvent failed", error)
    toast.add({
      title: "Quick-Eintrag konnte nicht angelegt werden",
      description: error?.message || "Bitte erneut versuchen.",
      color: "red"
    })
  }
}

function openAbsenceModal(type = "vacation", preset = {}) {
  absenceForm.mode = preset.entry ? "edit" : "create"
  absenceForm.entry = preset.entry || null
  absenceForm.type = type
  absenceForm.userId = preset.userId || profileOptions.value[0]?.value || ""
  absenceForm.startDate = preset.startDate || $dayjs().format("YYYY-MM-DD")
  absenceForm.endDate = preset.endDate || preset.startDate || $dayjs().format("YYYY-MM-DD")
  absenceForm.description = preset.description || ""
  isAbsenceModalOpen.value = true
}

async function saveAbsence() {
  if (!absenceForm.userId) {
    toast.add({ title: "Profil fehlt", description: "Bitte ein Profil auswählen.", color: "orange" })
    return
  }

  if (!absenceForm.startDate || !absenceForm.endDate) {
    toast.add({ title: "Zeitraum fehlt", description: "Bitte Start- und Enddatum angeben.", color: "orange" })
    return
  }

  if ($dayjs(absenceForm.endDate).isBefore($dayjs(absenceForm.startDate))) {
    toast.add({ title: "Zeitraum ungültig", description: "Das Enddatum muss am oder nach dem Startdatum liegen.", color: "orange" })
    return
  }

  savingAbsence.value = true

  try {
    const payload = {
      start: $dayjs(absenceForm.startDate).startOf("day").toISOString(),
      end: $dayjs(absenceForm.endDate).endOf("day").toISOString(),
      type: absenceForm.type,
      description: absenceForm.description || "",
      user_id: absenceForm.userId
    }

    if (absenceForm.mode === "edit" && absenceForm.entry) {
      await updateStaffTimeEntry(absenceForm.entry, payload)
      toast.add({ title: "Abwesenheit aktualisiert", color: "green" })
    } else {
      await createEntry(payload)
      toast.add({ title: absenceForm.type === "sick" ? "Krankmeldung eingetragen" : "Urlaub eingetragen", color: "green" })
    }

    isAbsenceModalOpen.value = false
    await loadPlanningBoard()
  } catch (error) {
    console.error("saveAbsence failed", error)
    toast.add({
      title: "Abwesenheit konnte nicht gespeichert werden",
      description: error?.message || "Bitte Eingaben prüfen und erneut versuchen.",
      color: "red"
    })
  } finally {
    savingAbsence.value = false
  }
}

async function loadPlanningBoard() {
  loading.value = true

  try {
    const [rawEvents, projects, profileResponse, inventoryItemRows] = await Promise.all([
      useEntities("events").select(),
      useEntities("projects").select(),
      useNuxtApp().$api("/api/tenant/profiles"),
      useEntities("inventoryitems").select()
    ])

    const profileRows = profileResponse?.data || []

    const absenceSpansByUserId = await Promise.all(
      (profileRows || [])
        .filter((profile) => profile.user_id)
        .map(async (profile) => ({
          userId: profile.user_id,
          spans: await listStaffTimeSpans({
            user_id: profile.user_id,
            from: visibleRange.value.from,
            to: visibleRange.value.to
          })
        }))
    )

    const projectsById = new Map((projects || []).map((project) => [project.id, project]))
    profiles.value = profileRows || []
    inventoryitems.value = inventoryItemRows || []

    if (!absenceForm.userId) {
      absenceForm.userId = profileOptions.value[0]?.value || ""
    }

    resources.value = buildResources({
      profiles: profiles.value,
      inventoryitems: inventoryitems.value
    })

    events.value = buildEvents({
      rawEvents: rawEvents || [],
      projectsById
    }).concat(buildAbsenceEvents(absenceSpansByUserId))
  } catch (error) {
    console.error("loadPlanningBoard failed", error)
    toast.add({
      title: "Plantafel konnte nicht geladen werden",
      description: "Die Ressourcen oder Planungen konnten nicht abgerufen werden.",
      color: "red"
    })
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  resetAbsenceForm()
  loadPlanningBoard()
})
</script>

<template>
  <div>
    <UDashboardNavbar title="Plantafel">
      <template #right>
        <UButton
          color="gray"
          variant="ghost"
          icon="i-heroicons-arrow-path"
          :loading="loading"
          @click="loadPlanningBoard"
        >
          Aktualisieren
        </UButton>
      </template>
    </UDashboardNavbar>

    <UDashboardToolbar>
      <template #left>
        <div class="flex flex-wrap items-center gap-3">
          <USelectMenu
            v-model="selectedType"
            :items="resourceTypeOptions"
            value-key="value"
            label-key="label"
            :clearable="false"
            class="min-w-[220px]"
          />
          <USelectMenu
            v-model="calendarView"
            :items="calendarViewOptions"
            value-key="value"
            label-key="label"
            :clearable="false"
            class="min-w-[160px]"
            @update:model-value="changeCalendarView"
          />
          <UPopover>
            <UButton
              color="neutral"
              variant="outline"
              icon="i-heroicons-calendar-days"
              class="min-w-[180px] justify-between"
            >
              {{ calendarCurrentDate ? $dayjs(calendarCurrentDate).format("DD.MM.YYYY") : "Datum wählen" }}
            </UButton>

            <template #content>
              <div class="p-2">
                <UCalendar v-model="calendarPickerValue" />
                <div class="flex justify-end border-t border-default pt-2">
                  <UButton color="neutral" variant="ghost" size="sm" @click="moveCalendarToday">
                    Heute
                  </UButton>
                </div>
              </div>
            </template>
          </UPopover>
          <div class="flex items-center gap-1">
            <UButton color="neutral" variant="ghost" icon="i-heroicons-chevron-left" @click="moveCalendar('prev')" />
            <UButton color="neutral" variant="ghost" icon="i-heroicons-chevron-right" @click="moveCalendar('next')" />
          </div>
          <span class="text-sm font-medium text-highlighted">
            {{ calendarTitle }}
          </span>
        </div>
      </template>
      <template #right>
        <div class="flex flex-wrap items-center gap-2">
          <UButton
            color="neutral"
            variant="outline"
            icon="i-heroicons-cog-6-tooth"
            @click="openQuickConfig"
          >
            Quick-Einträge
          </UButton>
          <UButton
            color="amber"
            variant="soft"
            icon="i-heroicons-sun"
            @click="openAbsenceModal('vacation')"
          >
            Urlaub
          </UButton>
          <UButton
            color="error"
            variant="soft"
            icon="i-heroicons-heart"
            @click="openAbsenceModal('sick')"
          >
            Krank
          </UButton>
        </div>
      </template>
    </UDashboardToolbar>

    <UDashboardPanelContent class="p-5">
      <div v-if="loading" class="space-y-3">
        <USkeleton class="h-12 w-full" />
        <USkeleton class="h-[70vh] w-full" />
      </div>

      <UAlert
        v-else-if="visibleResources.length === 0"
        icon="i-heroicons-calendar-days"
        title="Keine planbaren Ressourcen vorhanden"
        description="Lege Profile an oder aktiviere die Plantafel-Nutzung bei Inventarartikeln, damit hier Ressourcen erscheinen."
      />

      <FullCalendar
        v-else
        ref="calendarRef"
        :options="calendarOptions"
      />
    </UDashboardPanelContent>

    <UModal v-model:open="isQuickConfigModalOpen">
      <template #content>
        <UCard class="mx-auto w-full max-w-2xl" :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
          <template #header>
            <div class="flex items-center justify-between gap-4">
              <div>
                <h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
                  Quick-Einträge
                </h3>
                <p class="text-sm text-muted">
                  Name und Farbe für neu gezogene Quick-Einträge in der Plantafel.
                </p>
              </div>
              <UButton
                color="gray"
                variant="ghost"
                icon="i-heroicons-x-mark-20-solid"
                class="-my-1"
                @click="isQuickConfigModalOpen = false"
              />
            </div>
          </template>

          <div class="space-y-4 p-1">
            <div v-if="quickEntryPresets.length" class="space-y-2">
              <div class="text-xs font-medium text-muted">Vorlagen</div>
              <div class="flex flex-wrap gap-2">
                <button
                  v-for="preset in quickEntryPresets"
                  :key="preset.name"
                  type="button"
                  class="flex items-center gap-2 rounded border border-default px-2 py-1 text-xs transition hover:border-primary"
                  @click="applyQuickPreset(preset)"
                >
                  <span
                    class="h-4 w-4 rounded-full border border-white/40"
                    :style="{ backgroundColor: preset.color }"
                  />
                  {{ preset.name }}
                </button>
              </div>
            </div>

            <div class="rounded border border-default p-3">
              <button
                type="button"
                class="flex w-full items-center justify-between text-left"
                @click="showQuickConfigEditor = !showQuickConfigEditor"
              >
                <div>
                  <p class="text-sm font-medium text-highlighted">Konfiguration</p>
                  <p class="text-xs text-muted">Aktuellen Titel und Farbe für Quick-Einträge anpassen.</p>
                </div>
                <UIcon
                  :name="showQuickConfigEditor ? 'i-heroicons-chevron-up' : 'i-heroicons-chevron-down'"
                  class="text-muted"
                />
              </button>

              <div v-if="showQuickConfigEditor" class="mt-4 space-y-3 border-t border-default pt-4">
                <UFormField label="Titel">
                  <UInput v-model="quickEntryConfig.name" class="w-full" />
                </UFormField>

                <UFormField label="Vorlagenname">
                  <UInput v-model="newQuickPresetName" class="w-full" />
                </UFormField>

                <UFormField label="Farbe">
                  <div class="space-y-3">
                    <div class="flex flex-wrap gap-2">
                      <button
                        v-for="option in quickEntryColorOptions"
                        :key="option.value"
                        type="button"
                        class="flex items-center gap-2 rounded border px-2 py-1 text-xs transition"
                        :class="quickEntryConfig.color === option.value ? 'border-primary ring-1 ring-primary' : 'border-default'"
                        @click="quickEntryConfig.color = option.value"
                      >
                        <span
                          class="h-4 w-4 rounded-full border border-white/40"
                          :style="{ backgroundColor: option.value }"
                        />
                        {{ option.label }}
                      </button>
                    </div>

                    <div class="flex items-center gap-3">
                      <input
                        v-model="quickEntryConfig.color"
                        type="color"
                        class="h-10 w-14 cursor-pointer rounded border border-default bg-transparent p-1"
                      >
                      <UInput v-model="quickEntryConfig.color" class="flex-1" />
                    </div>
                  </div>
                </UFormField>

                <div class="rounded border border-default p-3">
                  <div class="flex items-start justify-between gap-3">
                    <div>
                      <p class="text-xs text-muted">Vorschau</p>
                      <div class="mt-2 inline-flex rounded px-3 py-1 text-sm font-medium text-white" :style="{ backgroundColor: quickEntryConfig.color }">
                        {{ quickEntryConfig.name || "Quick-Eintrag" }}
                      </div>
                    </div>
                    <UButton
                      color="primary"
                      :loading="savingQuickConfig"
                      @click="saveQuickPreset"
                    >
                      Vorlage speichern
                    </UButton>
                  </div>
                </div>
              </div>
            </div>

            <div class="rounded border border-default p-3">
              <button
                type="button"
                class="flex w-full items-center justify-between text-left"
                @click="showQuickPresetManagement = !showQuickPresetManagement"
              >
                <div>
                  <p class="text-sm font-medium text-highlighted">Vorlagen verwalten</p>
                  <p class="text-xs text-muted">Tenantweite Vorlagen löschen oder aufräumen.</p>
                </div>
                <UIcon
                  :name="showQuickPresetManagement ? 'i-heroicons-chevron-up' : 'i-heroicons-chevron-down'"
                  class="text-muted"
                />
              </button>

              <div v-if="showQuickPresetManagement" class="mt-4 space-y-4 border-t border-default pt-4">
                <div v-if="quickEntryPresets.length" class="space-y-2">
                  <p class="text-xs font-medium text-muted">Gespeicherte Vorlagen</p>
                  <div
                    v-for="preset in quickEntryPresets"
                    :key="`${preset.name}-${preset.color}`"
                    class="flex items-center justify-between gap-3 rounded border border-default px-3 py-2"
                  >
                    <button
                      type="button"
                      class="flex min-w-0 items-center gap-2 text-left"
                      @click="applyQuickPreset(preset)"
                    >
                      <span
                        class="h-4 w-4 shrink-0 rounded-full border border-white/40"
                        :style="{ backgroundColor: preset.color }"
                      />
                      <span class="truncate text-sm">{{ preset.name }}</span>
                    </button>
                    <UButton
                      color="error"
                      variant="ghost"
                      icon="i-heroicons-trash"
                      @click="deleteQuickPreset(preset.name)"
                    />
                  </div>
                </div>
              </div>
            </div>
          </div>
        </UCard>
      </template>
    </UModal>

    <UModal v-model:open="isAbsenceModalOpen">
      <template #content>
        <UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
          <template #header>
            <div class="flex items-center justify-between">
              <h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
                {{ absenceModalTitle }}
              </h3>
              <UButton
                color="gray"
                variant="ghost"
                icon="i-heroicons-x-mark-20-solid"
                class="-my-1"
                @click="isAbsenceModalOpen = false"
              />
            </div>
          </template>

        <div class="space-y-4">
          <UFormField label="Profil">
            <USelectMenu
              v-model="absenceForm.userId"
              :items="profileOptions"
              value-key="value"
              label-key="label"
              class="w-full"
              :search-input="{ placeholder: 'Profil suchen...' }"
              :filter-fields="['label']"
            />
          </UFormField>

          <UFormField label="Typ">
            <USelectMenu
              v-model="absenceForm.type"
              :items="absenceTypeOptions"
              value-key="value"
              label-key="label"
              class="w-full"
            />
          </UFormField>

          <div class="grid grid-cols-2 gap-4">
            <UFormField label="Start">
              <div class="flex items-center gap-2">
                <UPopover>
                  <UButton
                    color="neutral"
                    variant="outline"
                    icon="i-heroicons-calendar-days"
                    class="min-w-0 flex-1 justify-between"
                  >
                    <span class="truncate text-left">
                      {{ absenceForm.startDate ? $dayjs(absenceForm.startDate).format("DD.MM.YYYY") : "Kein Datum" }}
                    </span>
                  </UButton>

                  <template #content>
                    <div class="p-2">
                      <UCalendar v-model="startDateValue" />
                      <div class="flex justify-end border-t border-default pt-2">
                        <UButton color="neutral" variant="ghost" size="sm" @click="setAbsenceDateToToday('startDate')">
                          Heute
                        </UButton>
                      </div>
                    </div>
                  </template>
                </UPopover>

                <UButton color="neutral" variant="soft" label="Heute" @click="setAbsenceDateToToday('startDate')" />
              </div>
            </UFormField>
            <UFormField label="Ende">
              <div class="flex items-center gap-2">
                <UPopover>
                  <UButton
                    color="neutral"
                    variant="outline"
                    icon="i-heroicons-calendar-days"
                    class="min-w-0 flex-1 justify-between"
                  >
                    <span class="truncate text-left">
                      {{ absenceForm.endDate ? $dayjs(absenceForm.endDate).format("DD.MM.YYYY") : "Kein Datum" }}
                    </span>
                  </UButton>

                  <template #content>
                    <div class="p-2">
                      <UCalendar v-model="endDateValue" />
                      <div class="flex justify-end border-t border-default pt-2">
                        <UButton color="neutral" variant="ghost" size="sm" @click="setAbsenceDateToToday('endDate')">
                          Heute
                        </UButton>
                      </div>
                    </div>
                  </template>
                </UPopover>

                <UButton color="neutral" variant="soft" label="Heute" @click="setAbsenceDateToToday('endDate')" />
              </div>
            </UFormField>
          </div>

          <UFormField label="Notiz">
            <UTextarea
              v-model="absenceForm.description"
              :placeholder="absenceForm.type === 'sick' ? 'z. B. Krankmeldung eingegangen' : 'z. B. Sommerurlaub'"
            />
          </UFormField>
        </div>

          <template #footer>
            <div class="flex justify-end gap-2">
              <UButton color="gray" variant="soft" @click="isAbsenceModalOpen = false">
                Abbrechen
              </UButton>
              <UButton color="primary" :loading="savingAbsence" @click="saveAbsence">
                Speichern
              </UButton>
            </div>
          </template>
        </UCard>
      </template>
    </UModal>

    <UModal v-model:open="isProfileDetailsModalOpen">
      <template #content>
        <UCard :ui="{ ring: '', divide: 'divide-y divide-gray-100 dark:divide-gray-800' }">
          <template #header>
            <div class="flex items-center justify-between gap-3">
              <div>
                <h3 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">
                  {{ getProfileLabel(selectedProfile) }}
                </h3>
                <p class="text-sm text-gray-500 dark:text-gray-400">
                  Mitarbeiterdetails
                </p>
              </div>
              <UButton
                color="gray"
                variant="ghost"
                icon="i-heroicons-x-mark-20-solid"
                class="-my-1"
                @click="isProfileDetailsModalOpen = false"
              />
            </div>
          </template>

          <div v-if="selectedProfile" class="space-y-6">
            <div class="grid gap-4 md:grid-cols-2">
              <div class="rounded-lg border border-default p-4">
                <p class="text-xs font-medium uppercase tracking-wide text-muted">Kontakt</p>
                <dl class="mt-3 space-y-3 text-sm">
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">E-Mail</dt>
                    <dd class="text-right font-medium text-highlighted">{{ selectedProfile.email || "Nicht hinterlegt" }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Mobil</dt>
                    <dd class="text-right font-medium text-highlighted">{{ selectedProfile.mobile_tel || "Nicht hinterlegt" }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Festnetz</dt>
                    <dd class="text-right font-medium text-highlighted">{{ selectedProfile.fixed_tel || "Nicht hinterlegt" }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Geburtstag</dt>
                    <dd class="text-right font-medium text-highlighted">{{ formatProfileDate(selectedProfile.birthday) }}</dd>
                  </div>
                </dl>
              </div>

              <div class="rounded-lg border border-default p-4">
                <p class="text-xs font-medium uppercase tracking-wide text-muted">Beschäftigung</p>
                <dl class="mt-3 space-y-3 text-sm">
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">MA-Nummer</dt>
                    <dd class="text-right font-medium text-highlighted">{{ selectedProfile.employee_number || "Nicht hinterlegt" }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Position</dt>
                    <dd class="text-right font-medium text-highlighted">{{ selectedProfile.position || "Nicht hinterlegt" }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Vertragsart</dt>
                    <dd class="text-right font-medium text-highlighted">{{ selectedProfile.contract_type || "Nicht hinterlegt" }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Eintrittsdatum</dt>
                    <dd class="text-right font-medium text-highlighted">{{ formatProfileDate(selectedProfile.entry_date) }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Wochenstunden</dt>
                    <dd class="text-right font-medium text-highlighted">{{ formatProfileNumber(selectedProfile.weekly_working_hours) }}</dd>
                  </div>
                </dl>
              </div>
            </div>

            <div class="grid gap-4 md:grid-cols-2">
              <div class="rounded-lg border border-default p-4">
                <p class="text-xs font-medium uppercase tracking-wide text-muted">Zuordnung</p>
                <dl class="mt-3 space-y-3 text-sm">
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Niederlassung</dt>
                    <dd class="text-right font-medium text-highlighted">{{ selectedProfileBranchesLabel }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Teams</dt>
                    <dd class="text-right font-medium text-highlighted">{{ selectedProfileTeamsLabel }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Status</dt>
                    <dd class="text-right font-medium text-highlighted">{{ selectedProfile.active ? "Aktiv" : "Inaktiv" }}</dd>
                  </div>
                </dl>
              </div>

              <div class="rounded-lg border border-default p-4">
                <div class="flex items-start justify-between gap-4">
                  <div>
                    <p class="text-xs font-medium uppercase tracking-wide text-muted">Urlaub {{ currentVacationYear }}</p>
                    <p class="mt-1 text-sm text-muted">Anzeige auf Basis der aktuellen Zeitauswertung.</p>
                  </div>
                  <UBadge color="amber" variant="subtle">
                    Resturlaub
                  </UBadge>
                </div>

                <div v-if="loadingProfileVacation" class="mt-4 space-y-2">
                  <USkeleton class="h-6 w-full" />
                  <USkeleton class="h-6 w-4/5" />
                  <USkeleton class="h-6 w-3/5" />
                </div>

                <dl v-else class="mt-4 space-y-3 text-sm">
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Urlaubsanspruch</dt>
                    <dd class="text-right font-medium text-highlighted">{{ formatProfileNumber(selectedProfileAnnualLeaveDays) }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Bereits verplant/genehmigt</dt>
                    <dd class="text-right font-medium text-highlighted">{{ formatProfileNumber(selectedProfileVacationDaysTaken) }}</dd>
                  </div>
                  <div class="flex items-start justify-between gap-4">
                    <dt class="text-muted">Resturlaub</dt>
                    <dd class="text-right text-base font-semibold text-highlighted">
                      {{ selectedProfileRemainingVacationDays === null ? "Nicht hinterlegt" : formatProfileNumber(selectedProfileRemainingVacationDays) }}
                    </dd>
                  </div>
                </dl>
              </div>
            </div>
          </div>

          <template #footer>
            <div class="flex justify-end">
              <UButton color="gray" variant="soft" @click="isProfileDetailsModalOpen = false">
                Schließen
              </UButton>
            </div>
          </template>
        </UCard>
      </template>
    </UModal>
  </div>
</template>
