<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"

const router = useRouter()
const profileStore = useProfileStore()
const toast = useToast()
const { $dayjs } = useNuxtApp()
const { list: listStaffTimeSpans, createEntry, update: updateStaffTimeEntry } = useStaffTime()

const loading = ref(true)
const savingAbsence = ref(false)
const selectedType = ref("all")
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 isAbsenceModalOpen = ref(false)
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 resourceTypeOptions = [
  { label: "Alle Ressourcen", value: "all" },
  { label: "Profile", value: "Profile" },
  { label: "Inventarartikel", value: "Inventarartikel" }
]

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

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 visibleResources = computed(() => {
  if (selectedType.value === "all") return resources.value
  return resources.value.filter((resource) => resource.type === selectedType.value)
})

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: "resourceTimelineWeek",
  headerToolbar: {
    left: "prev,next today",
    center: "title",
    right: "resourceTimelineDay,resourceTimelineWeek,resourceTimelineMonth"
  },
  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"
    }
  ],
  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) {
    const resourceIds = info.resource?.id ? [info.resource.id] : []
    router.push(`/standardEntity/events/create?startDate=${encodeURIComponent(info.startStr)}&endDate=${encodeURIComponent(info.endStr)}&resources=${encodeURIComponent(JSON.stringify(resourceIds))}&source=timeline`)
  },
  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/show/${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}`

    if (nextKey === lastRangeKey.value) return

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

function resolveEventColor(eventType) {
  return profileStore.ownTenant?.calendarConfig?.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 "Planung"
}

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 buildResources({ profiles, inventoryitems }) {
  return [
    ...profiles
      .filter((profile) => !profile.archived)
      .map((profile) => ({
        id: `P-${profile.id}`,
        type: "Profile",
        title: getProfileLabel(profile)
      })),
    ...inventoryitems
      .filter((item) => !item.archived && item.usePlanning)
      .map((item) => ({
        id: `I-${item.id}`,
        type: "Inventarartikel",
        title: item.name
      }))
  ]
}

function buildEvents({ rawEvents, projectsById }) {
  const mappedEvents = rawEvents
    .filter((event) => !event.archived)
    .map((event) => {
      const resourceIds = [
        ...(event.profiles || []).map((profileId) => `P-${profileId}`),
        ...(event.inventoryitems || []).map((itemId) => `I-${itemId}`)
      ]

      return {
        title: resolveEventTitle(event, projectsById),
        start: event.startDate,
        end: event.endDate,
        resourceIds,
        backgroundColor: resolveEventColor(event.eventtype),
        borderColor: resolveEventColor(event.eventtype),
        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: [`P-${profile.id}`],
        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 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, profileRows, inventoryItemRows] = await Promise.all([
      useEntities("events").select(),
      useEntities("projects").select(),
      useEntities("profiles").select(),
      useEntities("inventoryitems").select()
    ])

    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"
            :options="resourceTypeOptions"
            value-attribute="value"
            option-attribute="label"
            :clearable="false"
            class="min-w-[220px]"
          />
        </div>
      </template>
      <template #right>
        <div class="flex flex-wrap items-center gap-2">
          <UButton
            color="amber"
            variant="soft"
            icon="i-heroicons-sun"
            @click="openAbsenceModal('vacation')"
          >
            Urlaub
          </UButton>
          <UButton
            color="rose"
            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
        :options="calendarOptions"
      />
    </UDashboardPanelContent>

    <UModal v-model:open="isAbsenceModalOpen">
      <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">
          <UFormGroup label="Profil">
            <USelectMenu
              v-model="absenceForm.userId"
              :options="profileOptions"
              value-attribute="value"
              option-attribute="label"
              searchable
            />
          </UFormGroup>

          <UFormGroup label="Typ">
            <USelectMenu
              v-model="absenceForm.type"
              :options="absenceTypeOptions"
              value-attribute="value"
              option-attribute="label"
            />
          </UFormGroup>

          <div class="grid grid-cols-2 gap-4">
            <UFormGroup label="Start">
              <div class="flex items-center gap-2">
                <UInput v-model="absenceForm.startDate" type="date" class="flex-1" />
                <UButton color="gray" variant="soft" label="Heute" @click="setAbsenceDateToToday('startDate')" />
              </div>
            </UFormGroup>
            <UFormGroup label="Ende">
              <div class="flex items-center gap-2">
                <UInput v-model="absenceForm.endDate" type="date" class="flex-1" />
                <UButton color="gray" variant="soft" label="Heute" @click="setAbsenceDateToToday('endDate')" />
              </div>
            </UFormGroup>
          </div>

          <UFormGroup label="Notiz">
            <UTextarea
              v-model="absenceForm.description"
              :placeholder="absenceForm.type === 'sick' ? 'z. B. Krankmeldung eingegangen' : 'z. B. Sommerurlaub'"
            />
          </UFormGroup>
        </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>
    </UModal>
  </div>
</template>
