<script>
import userService from '@/utils/user-utils'
import { RRule } from 'rrule'
import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'
import rrulePlugin from '@fullcalendar/rrule'
import axios from 'axios'

import {
  createEventId,
  showToastAtCursorOverlap,
  showToastAtCursorHoliday,
  showToastAtCursorLong,
  showToastAtCursorRecurring,
  showToastAtCursorForbidden,
  showToastAtCursorWarningAddAfter,
  showToastAtCursorWarningAddPrev,
  showToastAtCursorWarningChangePrev,
  showToastAtCursorWarningChangeAfter,
  showToastAtCursorWarningDelPrev,
  showToastAtCursorWarningDelAfter,
  showToastAtCursorWarningHours,
  showToastAtCursorWarningMonthMove,
  showToastAtCursorWarningAdminMove,
  showToastAtCursorWarningAutoFillFull,
  showToastAtCursorWarningAutoFillFull2,
  getHolidays
} from '@/utils/event-utils'

export default {
  components: {
    FullCalendar
  },
  // eslint-disable-next-line vue/multi-word-component-names
  name: 'Services',
  data() {
    return {
      screenWidth: window.innerWidth,
      // Nastavení kalendáře, jeho podmínek a metod
      calendarOptions: {
        plugins: [dayGridPlugin, interactionPlugin, rrulePlugin],
        headerToolbar: {
          left: 'title',
          center: window.innerWidth < 768 ? '' : 'dayGridMonth,dayGridWeek',
          right: 'prev next today'
        },
        buttonText: {
          today: 'Dnes',
          month: 'Měsíc',
          week: 'Týden'
        },
        firstDay: 1,
        locale: 'cs',
        timeZone: false,
        initialView: window.innerWidth < 768 ? 'dayGridWeek' : 'dayGridMonth',
        initialDate: this.getStoredDate(),
        weekends: true,
        selectable: true,
        editable: true,
        eventOverlap: false,
        eventDurationEditable: false,
        dayMaxEvents: true,
        select: this.handleEventAdd,
        eventClick: this.handleEventDelete,
        eventsSet: this.handleEventsSet,
        datesSet: this.handleDatesSet,
        eventChange: this.handleEventChange,
        eventMouseEnter: this.handleEventMouseEnter,
        eventMouseLeave: this.handleEventMouseLeave,
        events: [], // zde se bude plnit eventy z backendu
        // Obarvení buněk kalendáře
        dayCellClassNames: (info) => {
          const cellDate = info.date.toISOString().split('T')[0]
          const classes = []

          // Obarvení pro dny státních svátků
          const holidayClass = this.highlightHolidays(info.date)
          if (holidayClass) {
            classes.push(holidayClass)
          }

          // Obarvení pro buňky, které obsahují eventy
          if (this.datesWithEvents.includes(cellDate)) {
            classes.push('highlighted-cell')
          }

          return classes
        },
        eventContent: function (info) {
          // zaručuje, že zobrazené jméno eventu je čistě název title
          return { html: `<span>${info.event.title}</span>` }
        }
      },
      currentEvents: [],
      datesWithEvents: [],
      users: [],
      content: '',
      selectedUserId: '',
      isLoggedIn: false,
      isAdmin: false,
      loadAllEvents: false,
      adjacent: '',
      holiday: '',
      activeButton: 1
    }
  },
  created() {
    // Vyčistí notifikace
    window.toastr.remove()
  },
  computed: {
    // Metoda, která počítá celkový počet zbývajících hodin
    remainingWorkHours() {
      const totalMonthlyHours = 180
      const hoursPerEvent = 12
      const totalHours = totalMonthlyHours - this.currentEvents.length * hoursPerEvent
      return totalHours
    },
    // Metoda, která vrací zbývající počet dní do uzávěrky (15. den měsíce) a zároveň vrací jméno měsíce, kterého se uzávěrka týká
    remainingDaysUntilCutoff() {
      const today = new Date()
      let cutoffDate = new Date(today.getFullYear(), today.getMonth(), 15)

      // Pokud je dnešní den po datu uzávěrky, vypočítá pro následující měsíc
      if (today > cutoffDate) {
        cutoffDate.setMonth(cutoffDate.getMonth() + 1)
      }

      // Vypočítá rozdíl ve dnech
      const differenceInTime = cutoffDate.getTime() - today.getTime()
      const remainingDays = Math.ceil(differenceInTime / (1000 * 60 * 60 * 24))

      let appliesToMonth = new Date(cutoffDate)
      appliesToMonth.setMonth(appliesToMonth.getMonth() + 1)

      const monthName = appliesToMonth.toLocaleString('cs-CZ', { month: 'long' })

      return {
        remainingDays,
        month: monthName
      }
    }
  },
  methods: {
    async handleEventAdd(selectInfo) {
      if (this.loadAllEvents) {
        showToastAtCursorWarningAdminMove()
        selectInfo.view.calendar.unselect()
        return
      }

      const { start, end } = selectInfo
      const userId = this.selectedUserId || localStorage.getItem('userId')
      const today = new Date()
      const cutoffDate = new Date(today.getFullYear(), today.getMonth(), 15)
      const firstDayOfCurrentMonth = new Date(today.getFullYear(), today.getMonth(), 1)
      const lastDayOfCurrentMonth = new Date(
        today.getFullYear(),
        today.getMonth() + 1,
        0,
        23,
        59,
        59
      )

      const isForPastOrCurrentMonth =
        start < firstDayOfCurrentMonth ||
        (start >= firstDayOfCurrentMonth && start <= lastDayOfCurrentMonth && today < cutoffDate) ||
        (start >= firstDayOfCurrentMonth && start <= lastDayOfCurrentMonth && today > cutoffDate)

      if (isForPastOrCurrentMonth) {
        showToastAtCursorWarningAddPrev()
        selectInfo.view.calendar.unselect()
        return
      }

      const firstDayOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1)
      const lastDayOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 2, 0)

      // Zkontroluje jestli event je v dalším měsíci
      const isForNextMonth = start >= firstDayOfNextMonth && start <= lastDayOfNextMonth
      // Zkontroluje aby eventy pro následující měsíc byly povoleny pouze před datem uzávěrky (15.den)
      if (isForNextMonth && today > cutoffDate) {
        showToastAtCursorWarningAddAfter()
        selectInfo.view.calendar.unselect()
        return
      }

      const hasOverlap = this.calendarOptions.events.some((event) => {
        const eventStart = new Date(event.start)
        const eventEnd = new Date(event.end)
        const eventUserId = event.extendedProps?.userId || null
        const currentUserId = userId

        // Pokud vytvářený event se nachází v již existujícím, tak se overlapují
        const isOverlapping = start < eventEnd && end > eventStart

        let isAdjacentBlocked = false
        // Pokud je zaškrtnutá možnost zakázání 2 dnů po sobě jdoucích
        // Tedy pokud je event buď před nebo za již vytvořeným eventem -> nevztahuje se na jiné uživatele stejné skupiny
        if (this.adjacent) {
          if (eventUserId === currentUserId && currentUserId !== null) {
            const prevDay = new Date(start)
            prevDay.setDate(prevDay.getDate() - 1)

            const nextDay = new Date(start)
            nextDay.setDate(nextDay.getDate() + 1)

            const isAdjacent =
              eventStart.getTime() === prevDay.getTime() ||
              eventStart.getTime() === nextDay.getTime()

            if (isAdjacent) {
              isAdjacentBlocked = true
            }
          }
        }
        return isOverlapping || isAdjacentBlocked
      })

      if (hasOverlap) {
        showToastAtCursorOverlap()
        selectInfo.view.calendar.unselect()
        return
      }

      const currentMonth = start.getMonth()
      const currentYear = start.getFullYear()
      let weekendHolidayEventsCount = 0
      const currentUserId = userId
      const newEventDate = start.toISOString().split('T')[0]
      const newEventDay = start.getDay()

      const holidayBaseDates = getHolidays()

      this.calendarOptions.events.forEach((event) => {
        const eventStart = new Date(event.start)
        const eventUserId = event.extendedProps?.userId || null
        const eventDateStr = eventStart.toISOString().split('T')[0]
        const eventDay = eventStart.getDay() // 0 = sobota, 6 = neděle

        if (
          eventUserId === currentUserId &&
          eventStart.getMonth() === currentMonth &&
          eventStart.getFullYear() === currentYear &&
          (eventDay === 0 || eventDay === 6 || holidayBaseDates.has(eventDateStr))
        ) {
          weekendHolidayEventsCount++
        }
      })

      if (
        this.holiday &&
        (newEventDay === 0 || newEventDay === 6 || holidayBaseDates.has(newEventDate))
      ) {
        weekendHolidayEventsCount++

        if (weekendHolidayEventsCount > 2) {
          showToastAtCursorHoliday()
          selectInfo.view.calendar.unselect()
          this.handleEventMouseLeave()
          return
        }
      }

      // Limit směn na každý měsíc, nemůže jít do mínusu počet hodin
      if (this.remainingWorkHours <= 0) {
        showToastAtCursorWarningHours()
        selectInfo.view.calendar.unselect()
        return
      }

      // Výpočet rozdílu vybraných datumů
      const diffInTime = end - start
      const diffInDays = diffInTime / (1000 * 60 * 60 * 24)

      // Pokud je vybráno více než 1 den najednou, tak se zruší select.
      if (diffInDays > 1) {
        showToastAtCursorLong()
        selectInfo.view.calendar.unselect()
      } else {
        let title = 'Noční'
        let calendarApi = selectInfo.view.calendar

        calendarApi.unselect()

        if (title) {
          if (!userId) {
            console.error('Uživatelovo ID nenalezeno, ujistěte se, že je uživatel přihlášen')
            return
          }

          let repeated = this.activeButton === 2

          // úprava endDate, aby se jednalo vždy o 1 denní event
          let adjustedEnd = new Date(selectInfo.end)
          adjustedEnd.setMilliseconds(adjustedEnd.getMilliseconds() - 1000)

          let eventObject = {
            id: createEventId(),
            title,
            start: new Date(selectInfo.start),
            end: adjustedEnd,
            userId,
            repeated
          }

          if (repeated) {
            if (!eventObject.rrule) {
              eventObject.rrule = {
                freq: RRule.MONTHLY,
                dtstart: eventObject.start,
                until: new Date(
                  new Date(eventObject.start).setFullYear(
                    new Date(eventObject.start).getFullYear() + 1
                  )
                ),
                bymonthday: [eventObject.start.getDate()]
              }
            }
            eventObject.textColor = '#000'
            eventObject.classNames = ['recurring']

            // Definuj opakovací pravidlo pokud je `activeButton` 2 -> Tlačítko "Měsíčně"
          }

          const newEvent = calendarApi.addEvent(eventObject)
          this.calendarOptions.events.push(eventObject)
          this.updateDatesWithEvents(eventObject)

          try {
            console.log('Formatted Start Date:', eventObject.start.toISOString())
            console.log('Formatted End Date:', eventObject.end.toISOString())

            await axios.post('http://localhost:3001/api/events', {
              id: newEvent.id,
              title: newEvent.title,
              start: eventObject.start.toISOString(),
              end: eventObject.end.toISOString(),
              userId,
              repeated: eventObject.repeated,
              rrule: eventObject.rrule || null
            })
            // reloaduje eventy z backendu pro konzistenci v kalendáři
            if (this.selectedUserId) {
              await this.loadEventsFromBackend(this.selectedUserId)
            } else {
              await this.loadEventsFromBackend()
            }

            console.log('Událost byla přidána a poslána na server')
          } catch (error) {
            console.error('Nepodařilo se uložit událost na server', error)
            newEvent.remove()
            this.calendarOptions.events = this.calendarOptions.events.filter(
              (event) => event.id !== eventObject.id
            )
          }
        }
      }
    },
    async handleEventDelete(clickInfo) {
      if (this.loadAllEvents) {
        showToastAtCursorWarningAdminMove()
        clickInfo.revert()
        return
      }
      const { start } = clickInfo.event
      const today = new Date()
      const cutoffDate = new Date(today.getFullYear(), today.getMonth(), 15)
      const firstDayOfCurrentMonth = new Date(today.getFullYear(), today.getMonth(), 1)
      const lastDayOfCurrentMonth = new Date(
        today.getFullYear(),
        today.getMonth() + 1,
        0,
        23,
        59,
        59
      )

      const isForPastOrCurrentMonth =
        start < firstDayOfCurrentMonth ||
        (start >= firstDayOfCurrentMonth && start <= lastDayOfCurrentMonth && today < cutoffDate) ||
        (start >= firstDayOfCurrentMonth && start <= lastDayOfCurrentMonth && today > cutoffDate)

      // Zabrání vymazání eventy z minulosti nebo z aktuálního měsíce po 15. dni.
      if (isForPastOrCurrentMonth) {
        showToastAtCursorWarningDelPrev()
        return
      }

      const firstDayOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1)
      const lastDayOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 2, 0)

      // Zkontroluje jestli event je v dalším měsíci
      const isForNextMonth = start >= firstDayOfNextMonth && start <= lastDayOfNextMonth
      // Zkontroluje aby eventy pro následující měsíc byly možné smazat pouze před datem uzávěrky (15.den)
      if (isForNextMonth && today > cutoffDate) {
        showToastAtCursorWarningDelAfter()
        return
      }

      const loggedInUserId = localStorage.getItem('userId')
      const eventUserId = clickInfo.event.extendedProps.userId

      if (eventUserId !== loggedInUserId && !this.isAdmin) {
        showToastAtCursorForbidden()
        return
      }

      if (confirm(`Jste si jistí, že chcete smazat tuto událost? '${clickInfo.event.title}'`)) {
        const eventId = clickInfo.event.id
        clickInfo.event.remove()
        this.calendarOptions.events = this.calendarOptions.events.filter(
          (event) => event.id !== eventId
        )
        this.removeDatesFromEvents(clickInfo.event)
        this.handleEventMouseLeave()

        try {
          await axios.delete(`http://localhost:3001/api/events/${eventId}`)
          if (this.selectedUserId) {
            await this.loadEventsFromBackend(this.selectedUserId)
          } else {
            await this.loadEventsFromBackend()
          }
          console.log('Událost byla odstraněna a změna byla poslána na server')
        } catch (error) {
          console.error('Nepodařilo se odstranit událost uloženou na serveru', error)
          this.loadEventsFromBackend()
        }
      }
    },
    async handleEventChange(changeInfo) {
      if (this.loadAllEvents) {
        showToastAtCursorWarningAdminMove()
        changeInfo.revert()
        return
      }
      // Pokud se jedná o opakující se event (recurring) dle vlastností "repeated == true" nebo pokud má vlastnost rrule, tak se akce vyruší
      if (changeInfo.event.extendedProps.repeated || changeInfo.event.extendedProps.rrule) {
        showToastAtCursorRecurring()
        this.handleEventMouseLeave()
        changeInfo.revert()
        return
      }

      const { start, end } = changeInfo.event
      const userId = this.selectedUserId || localStorage.getItem('userId')
      const today = new Date()
      const cutoffDate = new Date(today.getFullYear(), today.getMonth(), 15)
      const firstDayOfCurrentMonth = new Date(today.getFullYear(), today.getMonth(), 1)
      const lastDayOfCurrentMonth = new Date(
        today.getFullYear(),
        today.getMonth() + 1,
        0,
        23,
        59,
        59
      )

      const isForPastOrCurrentMonth =
        start < firstDayOfCurrentMonth ||
        (start >= firstDayOfCurrentMonth && start <= lastDayOfCurrentMonth && today < cutoffDate) ||
        (start >= firstDayOfCurrentMonth && start <= lastDayOfCurrentMonth && today > cutoffDate)

      if (isForPastOrCurrentMonth) {
        showToastAtCursorWarningChangePrev()
        changeInfo.revert() // Vrátí změnu
        this.handleEventMouseLeave()
        return
      }

      const firstDayOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1)
      const lastDayOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 2, 0)

      // Zkontroluje jestli event je v dalším měsíci
      const isForNextMonth = start >= firstDayOfNextMonth && start <= lastDayOfNextMonth

      if (isForNextMonth && today > cutoffDate) {
        showToastAtCursorWarningChangeAfter()
        changeInfo.revert()
        this.handleEventMouseLeave()
        return
      }

      const hasOverlap = this.calendarOptions.events.some((event) => {
        const eventStart = new Date(event.start)
        const eventEnd = new Date(event.end)
        const eventUserId = event.extendedProps?.userId || null
        const currentUserId = userId

        // Neaplikuje pravidlo overlapu event sám na sebe
        if (event.id === changeInfo.event.id) return false

        // Pokud se event nachází v již existujícím, tak se overlapují
        const isOverlapping = start < eventEnd && end > eventStart

        let isAdjacentBlocked = false
        // Pokud je zaškrtnutá možnost zakázání 2 dnů po sobě jdoucích
        // Tedy pokud je event buď před nebo za již vytvořeným eventem -> nevztahuje se na jiné uživatele stejné skupiny
        if (this.adjacent) {
          if (eventUserId === currentUserId && currentUserId !== null) {
            const prevDay = new Date(start)
            prevDay.setDate(prevDay.getDate() - 1)

            const nextDay = new Date(start)
            nextDay.setDate(nextDay.getDate() + 1)

            const isAdjacent =
              eventStart.getTime() === prevDay.getTime() ||
              eventStart.getTime() === nextDay.getTime()

            if (isAdjacent) {
              isAdjacentBlocked = true
            }
          }
        }
        return isOverlapping || isAdjacentBlocked
      })

      if (hasOverlap) {
        showToastAtCursorOverlap()
        changeInfo.revert()
        this.handleEventMouseLeave()
        return
      }

      // Pravidlo max 2 víkend dny/státní svátky
      const currentMonth = start.getMonth()
      const currentYear = start.getFullYear()
      let weekendHolidayEventsCount = 0
      const currentUserId = userId
      const newEventDate = start.toISOString().split('T')[0]
      const newEventDay = start.getDay()

      const holidayBaseDates = getHolidays()

      this.calendarOptions.events.forEach((event) => {
        const eventStart = new Date(event.start)
        const eventUserId = event.extendedProps?.userId || null
        const eventDateStr = eventStart.toISOString().split('T')[0]
        const eventDay = eventStart.getDay() // 0 = sobota, 6 = neděle

        // Neaplikuje pravidlo event sám na sebe
        if (event.id === changeInfo.event.id) return false

        if (
          eventUserId === currentUserId &&
          eventStart.getMonth() === currentMonth &&
          eventStart.getFullYear() === currentYear &&
          (eventDay === 0 || eventDay === 6 || holidayBaseDates.has(eventDateStr))
        ) {
          weekendHolidayEventsCount++
        }
      })

      if (
        this.holiday &&
        (newEventDay === 0 || newEventDay === 6 || holidayBaseDates.has(newEventDate))
      ) {
        weekendHolidayEventsCount++

        if (weekendHolidayEventsCount > 2) {
          showToastAtCursorHoliday()
          changeInfo.revert()
          this.handleEventMouseLeave()
          return
        }
      }

      // Zákaz přesouvání směny mezi jednotlivými měsíci
      const oldStart = changeInfo.oldEvent.start
      const newStart = changeInfo.event.start
      if (oldStart.getMonth() !== newStart.getMonth()) {
        showToastAtCursorWarningMonthMove()
        this.handleEventMouseLeave()
        changeInfo.revert()
        return
      }

      try {
        await axios.put(`http://localhost:3001/api/events/${changeInfo.event.id}`, {
          id: changeInfo.event.id,
          title: changeInfo.event.title,
          start: changeInfo.event.start,
          end: changeInfo.event.end
        })
        console.log('Událost byla úspěšně aktualizována a poslána na server')

        const updatedEvent = this.calendarOptions.events.find(
          (event) => event.id === changeInfo.event.id
        )
        this.removeDatesFromEvents(updatedEvent)
        if (updatedEvent) {
          updatedEvent.start = changeInfo.event.start
          updatedEvent.end = changeInfo.event.end
        }
        this.updateDatesWithEvents(updatedEvent)
        this.handleEventMouseLeave()
      } catch (error) {
        console.log('Update události se nepodařil', error)
        changeInfo.revert()
      }
    },
    // Pomocná metoda, která zjišťuje jestli jsou eventy součásti daného měsíce
    handleEvents(events, viewDates) {
      const userId = localStorage.getItem('userId')
      if (!userId) {
        console.error('Uživatelovo ID nenalezeno, ujištěte se, že je uživatel přihlášen')
        return
      }

      const firstDayOfMonth = viewDates.start
      const lastDayOfMonth = viewDates.end

      // Filtruje pole eventů a určuje jestli jsou součástí zobrazeného měsíce
      // Pokud nalezený event patří přihlášenému uživateli, tak vrací hodnotu True
      this.currentEvents = events.filter((event) => {
        const eventStart = new Date(event.start)

        var belongstToUser = event.extendedProps?.userId === userId

        // Ošetření pokud se jedná o admina, který si prohlíží eventy ostatnich uživatelů
        if (this.isAdmin) {
          belongstToUser = event.extendedProps?.userId === this.selectedUserId
        }

        const isWithinMonth = eventStart >= firstDayOfMonth && eventStart <= lastDayOfMonth

        return belongstToUser && isWithinMonth
      })
    },

    async loadEventsFromBackend(userId = null) {
      try {
        let apiUrl = 'http://localhost:3001/api/events/all'
        if (!this.loadAllEvents) {
          // zjišťuje jestli je nějaký uživatel přihlášen
          var effectiveUserId = userId || localStorage.getItem('userId')
          if (!effectiveUserId) {
            console.error('Uživatelovo ID nenalezeno, ujistěte se, že je uživatel přihlášen')
            return
          }
          apiUrl = `http://localhost:3001/api/events?userId=${effectiveUserId}`
        }

        const response = await axios.get(apiUrl)

        if (!response.data || !response.data.events) {
          console.error('Neplatná odpověď od serveru:', response.data)
          return
        }

        this.datesWithEvents = []

        const transformedEvents = response.data.events.map((event) => {
          this.updateDatesWithEvents(event)

          if (event.userId === effectiveUserId) {
            return {
              ...event,
              extendedProps: {
                userId: event.userId
              },
              // Přidá specifickou styling třídu pro opakující i neopakující se eventy
              classNames: event.rrule ? ['recurring'] : ['non-recurring']
            }
          } else {
            // Použije styling na eventy, které nepatří přihlášenému uživateli
            // Pokud se jedná o admin uživatele, tak mu povolí editaci eventů ostatnich eventů
            if (this.isAdmin) {
              return {
                ...event,
                classNames: ['group-member-event'],
                editable: true
              }
            }
            return {
              ...event,
              classNames: ['group-member-event'],
              editable: false
            }
          }
        })

        this.calendarOptions.events = transformedEvents

        console.log('Události byli načteny ze serveru')
      } catch (error) {
        console.error('Nepodařilo se načíst události ze serveru', error)
      }
    },
    // Metoda, která aktualizuje pole datesWithEvents tak, aby zahrnovala všechna datumy, kterých se event týká
    // Neaktualizuje pokud datumy jsou státními svátky -> pro lepší přehled v kalendáři
    updateDatesWithEvents(event) {
      const eventStart = new Date(event.start)
      const formattedDate = eventStart.toISOString().split('T')[0]

      const holidaysSet = getHolidays()
      const holidays = Array.from(holidaysSet)

      if (!this.datesWithEvents.includes(formattedDate) && !holidays.includes(formattedDate)) {
        this.datesWithEvents.push(formattedDate)
      }
    },
    // Metoda pro odstranění datumů spojených s odstraňovaným eventem z pole datesWithEvents
    removeDatesFromEvents(event) {
      const eventStart = new Date(event.start)
      let currentDate = new Date(eventStart)
      const formattedDate = currentDate.toISOString().split('T')[0]
      const index = this.datesWithEvents.indexOf(formattedDate)
      if (index > -1) {
        this.datesWithEvents.splice(index, 1)
      }
    },
    setActiveButton(buttonNumber) {
      this.activeButton = buttonNumber
    },
    // Zpracovává datumy svátků a následně jim přidává červené pozadí v kalendáři po dobu 30 let
    highlightHolidays(date) {
      // Pomocná funkce ke generaci datumů pro svátky
      const generateHolidayDates = (baseDate) => {
        const holidayDates = []
        const baseYear = parseInt(baseDate.split('-')[0])
        const monthAndDay = baseDate.slice(5)

        // Generuje datumy svátků každý rok po dobu 30 let
        for (let i = 0; i < 30; i++) {
          const year = baseYear + i
          holidayDates.push(`${year}-${monthAndDay}`) // přidá do seznamu datumy svátků
        }
        return holidayDates
      }

      // Pole, které obsahuje přesné datumy svátků, které se mají obarvit
      const holidayBaseDates = [
        '2024-05-01',
        '2024-05-08',
        '2024-07-05',
        '2024-07-06',
        '2024-09-28',
        '2024-10-28',
        '2024-11-17',
        '2024-12-24',
        '2024-12-25',
        '2024-12-26',
        '2025-01-01'
      ]
      let nationalHolidays = []
      holidayBaseDates.forEach((baseDate) => {
        nationalHolidays = nationalHolidays.concat(generateHolidayDates(baseDate))
      })

      // Získá datum aktuální buňky v místín čase a zformátuje ho tak, aby odpovídal základnímu formátu datumu
      const cellDate = date.toLocaleDateString('en-CA')
      // Zkontroluje jestli datum aktuální buňky se shoduje s datumem svátků, pokud ano vrací přiřazený styling
      if (nationalHolidays.includes(cellDate)) {
        return 'holiday-cell'
      }
      return ''
    },
    // Metoda, která zjišťuje začátek a konec každého zobrazeného měsíce
    handleDatesSet(view) {
      const displayedDate = new Date(view.start)
      displayedDate.setHours(displayedDate.getHours() - 2)
      const firstDayOfMonth = new Date(displayedDate.getFullYear(), displayedDate.getMonth() + 1, 1)
      const lastDayOfMonth = new Date(
        displayedDate.getFullYear(),
        displayedDate.getMonth() + 2,
        0,
        23,
        59,
        59
      )
      localStorage.setItem('lastViewedDate', lastDayOfMonth)
      const firstDayOfMonth2 = new Date(displayedDate.getFullYear(), displayedDate.getMonth(), 1)
      const lastDayOfMonth2 = new Date(
        displayedDate.getFullYear(),
        displayedDate.getMonth() + 1,
        0,
        23,
        59,
        59
      )
      // Podmínka pokud view měsíce začíná stejným datumem jako je začátek měsíce, tak je start a end posunut, aby nedošlo k bugu
      if (displayedDate.getTime() == firstDayOfMonth2.getTime()) {
        this.viewDates = { start: firstDayOfMonth2, end: lastDayOfMonth2 }
        localStorage.setItem('firstViewedDate', firstDayOfMonth2)
      } else {
        this.viewDates = { start: firstDayOfMonth, end: lastDayOfMonth }
        localStorage.setItem('firstViewedDate', firstDayOfMonth)
      }
    },
    // Pomocná metoda, která získá uložené datum z localStorage a konvertuje ho na formát, který tato metoda vrací
    getStoredDate() {
      const storedDate = localStorage.getItem('lastViewedDate')
      if (storedDate) {
        const parsedDate = new Date(storedDate)
        return parsedDate.toISOString().split('T')[0] // Convert to "YYYY-MM-DD"
      }
      return new Date().toISOString().split('T')[0] // Default to today
    },
    handleEventsSet(events) {
      if (this.viewDates) {
        this.handleEvents(events, this.viewDates)
      }
    },
    // Pokud uživatel přejede přes event, zobrazí se nápověda
    handleEventMouseEnter(info) {
      this.handleEventMouseLeave()
      const userId = info.event.extendedProps?.userId
      const user = this.users.find((user) => user._id === userId)

      const userName = user ? user.username : 'Neznámý uživatel'

      // Vytvoří prvek nápovědy
      const tooltip = document.createElement('div')
      tooltip.classList.add('event-tooltip')
      tooltip.innerText = `Vytvořil: ${userName}`

      // Umístí nápovědu na pozici kurzoru
      tooltip.style.top = `${info.jsEvent.pageY + 10}px`
      tooltip.style.left = `${info.jsEvent.pageX + 10}px`

      // Přiřadí ID pro pozdější odstranění
      tooltip.id = 'calendar-tooltip'
      document.body.appendChild(tooltip)
    },
    // Pomocná metoda, která odstraní nápovědu, když uživatel přejede kurzorem z eventu
    handleEventMouseLeave() {
      const tooltip = document.getElementById('calendar-tooltip')
      if (tooltip) {
        tooltip.remove()
      }
    },
    // Metoda, která načítá preference přihlášeného uživatele
    async loadUserPreferences(userId = null) {
      try {
        if (userId == null) {
          const effectiveUserId = localStorage.getItem('userId')
          const response = await axios.get(
            `http://localhost:3001/api/user/preferences/${effectiveUserId}`
          )
          this.holiday = response.data.holiday
          this.adjacent = response.data.adjacent
        } else {
          // Předá mu zvolené userId, pro zobrazení preferencí jednotlivých uživatelů -> admin only
          const effectiveUserId = userId
          const response = await axios.get(
            `http://localhost:3001/api/user/preferences/${effectiveUserId}`
          )
          this.holiday = response.data.holiday
          this.adjacent = response.data.adjacent
        }
      } catch (error) {
        console.error('Error při načítání preferencí:', error)
      }
    },

    // Metoda, která ukládá preference přihlášeného uživatele
    async saveUserPreferences(userId = null) {
      try {
        if (userId == null) {
          const effectiveUserId = localStorage.getItem('userId')
          await axios.put(`http://localhost:3001/api/user/preferences/${effectiveUserId}`, {
            holiday: this.holiday,
            adjacent: this.adjacent
          })
        } else {
          // Předá mu zvolené userId, pro zobrazení preferencí jednotlivých uživatelů -> admin only
          const effectiveUserId = userId
          await axios.put(`http://localhost:3001/api/user/preferences/${effectiveUserId}`, {
            holiday: this.holiday,
            adjacent: this.adjacent
          })
        }
      } catch (error) {
        console.error('Error při ukládání preferencí', error)
      }
    },

    async fetchUsers() {
      try {
        const response = await axios.get('http://localhost:3001/api/users/')
        this.users = response.data
      } catch (error) {
        console.error('Nepodařilo se získat uživatele z backendu', error)
      }
    },
    // Načítá rozvrhy eventů jednotlivých uživatelů -> admin only
    async loadUserEvents() {
      this.loadAllEvents = false
      if (!this.selectedUserId) return
      await this.loadEventsFromBackend(this.selectedUserId)
      this.loadUserPreferences(this.selectedUserId)
    },
    // Pomocná metoda, která načítá úplně všechny dostupné eventy
    toggleEvents() {
      this.loadEventsFromBackend()
      this.selectedUserId = ''
    },
    // Pomocná metoda, která ukládá nastavení preferencí pro sousední dny
    toggleAdjacentRule() {
      this.calendarOptions.events = [...this.calendarOptions.events]
      // Pokud je vybrán uživatel -> pro admina
      if (this.selectedUserId) {
        this.saveUserPreferences(this.selectedUserId)
      } else {
        this.saveUserPreferences()
      }
    },
    // Pomocná metoda, která ukládá nastavení preferencí pro víkendy/svátky
    toggleHolidayRule() {
      this.calendarOptions.events = [...this.calendarOptions.events]
      // Pokud je vybrán uživatel -> pro admina
      if (this.selectedUserId) {
        this.saveUserPreferences(this.selectedUserId)
      } else {
        this.saveUserPreferences()
      }
    },
    // Metoda, která vytváří eventy automaticky při stisknutí tlačítka a zároveň dodržuje všechny preference
    async autoFillEvents() {
      const userId = localStorage.getItem('userId')
      const firstDayOfMonth = new Date(localStorage.getItem('firstViewedDate'))
      const lastDayOfMonth = new Date(localStorage.getItem('lastViewedDate'))
      const holidayBaseDates = new Set(getHolidays())
      const today = new Date()
      const cutoffDate = new Date(today.getFullYear(), today.getMonth(), 15)
      const firstDayOfCurrentMonth = new Date(today.getFullYear(), today.getMonth(), 1)
      const lastDayOfCurrentMonth = new Date(
        today.getFullYear(),
        today.getMonth() + 1,
        0,
        23,
        59,
        59
      )

      const isForPastOrCurrentMonth =
        firstDayOfMonth < firstDayOfCurrentMonth ||
        (firstDayOfMonth >= firstDayOfCurrentMonth &&
          firstDayOfMonth <= lastDayOfCurrentMonth &&
          today < cutoffDate) ||
        (firstDayOfMonth >= firstDayOfCurrentMonth &&
          firstDayOfMonth <= lastDayOfCurrentMonth &&
          today > cutoffDate)

      if (isForPastOrCurrentMonth) {
        showToastAtCursorWarningAddPrev()
        return
      }

      const firstDayOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 1, 1)
      const lastDayOfNextMonth = new Date(today.getFullYear(), today.getMonth() + 2, 0)

      // Zkontroluje jestli event je v dalším měsíci
      const isForNextMonth =
        firstDayOfMonth >= firstDayOfNextMonth && firstDayOfMonth <= lastDayOfNextMonth
      // Zkontroluje aby eventy pro následující měsíc byly povoleny pouze před datem uzávěrky (15.den)
      if (isForNextMonth && today > cutoffDate) {
        showToastAtCursorWarningAddAfter()
        return
      }

      if (this.loadAllEvents) {
        showToastAtCursorWarningAdminMove()
        return
      }

      try {
        const response = await axios.get(`http://localhost:3001/api/events/?userId=${userId}`)
        const allEvents = Array.isArray(response.data) ? response.data : response.data.events

        // Filtruje eventy přihlášeného uživatele pro momentální měsíc
        const userEvents = allEvents.filter((event) => {
          const belongstToUser = event.userId === userId
          const eventDate = new Date(event.start)
          return belongstToUser && eventDate >= firstDayOfMonth && eventDate <= lastDayOfMonth
        })

        const missingEventsCount = 15 - userEvents.length
        if (missingEventsCount <= 0) {
          showToastAtCursorWarningAutoFillFull2()
          return
        }

        let newEvents = []
        let weekendHolidayEventsCount = userEvents.filter((event) => {
          const eventStart = new Date(event.start)
          const eventDay = eventStart.getDay()
          const eventDateStr = eventStart.toISOString().split('T')[0]

          return eventDay === 0 || eventDay === 6 || holidayBaseDates.has(eventDateStr)
        }).length

        let dayIterator = new Date(firstDayOfMonth)

        while (newEvents.length < missingEventsCount && dayIterator <= lastDayOfMonth) {
          dayIterator.setHours(0, 0, 0, 0)

          const startDate = new Date(dayIterator)
          const endDate = new Date(dayIterator.setHours(23, 59, 59, 0))

          const newEventDay = startDate.getDay()
          const newEventDateStr = startDate.toISOString().split('T')[0]

          // Prochází všechny eventy i ty nově vytvořené a kontroluje, jestli se overlappují s těmi právě vytvářenými
          const hasOverlap = [...allEvents, ...newEvents].some((event) => {
            const eventStart = new Date(event.start)
            const eventEnd = new Date(event.end)
            // Normalizace datumů a časů
            eventEnd.setDate(eventEnd.getDate() - 1)
            eventStart.setHours(0, 0, 0, 0)
            eventEnd.setHours(23, 59, 59, 0)

            return startDate < eventEnd && endDate > eventStart
          })

          // Podmínka, která posílá dál pouze eventy, které se neoverlappují
          // Pokud je zašrtnutá preference zakazující 2 dny po sobě, tak blokuje generaci sousedních eventů
          if (!hasOverlap) {
            let isAdjacentBlocked = false
            if (this.adjacent) {
              const prevDay = new Date(startDate)
              prevDay.setDate(prevDay.getDate() - 1)

              const nextDay = new Date(startDate)
              nextDay.setDate(nextDay.getDate() + 1)

              isAdjacentBlocked = [...userEvents, ...newEvents].some((event) => {
                const eventStart = new Date(event.start)
                return (
                  eventStart.toDateString() === prevDay.toDateString() ||
                  eventStart.toDateString() === nextDay.toDateString()
                )
              })
            }

            let isHolidayOrWeekend =
              newEventDay === 0 || newEventDay === 6 || holidayBaseDates.has(newEventDateStr)

            if (this.holiday && isHolidayOrWeekend) {
              if (weekendHolidayEventsCount >= 2) {
                // Posune se o jeden den
                dayIterator.setDate(dayIterator.getDate() + 1)
                continue
              } else {
                weekendHolidayEventsCount++
              }
            }

            if (!isAdjacentBlocked) {
              newEvents.push({
                id: createEventId(),
                title: 'Aut. služba',
                start: startDate,
                end: endDate,
                userId: userId,
                repeated: false,
                rrule: null
              })
            }
          }
          dayIterator.setDate(dayIterator.getDate() + 1)
        }

        if (newEvents.length === 0) {
          showToastAtCursorWarningAutoFillFull()
        }

        if (newEvents.length > 0) {
          await axios.post('http://localhost:3001/api/events/array', { events: newEvents })
          this.loadEventsFromBackend()
          console.log('Události byly automaticky přidány!')
        }
      } catch (error) {
        console.log('Chyba při automatickém přidání událostí:', error)
      }
    }
  },

  // po načtení stránky se zkontroluje localStorage, pokud se shoduje poslaný access token a jméno uživatele
  // nejdřív se zkontroluje jestli se jedná o admin uživatele -> pokud ano -> zobrazí admin obsah
  // poté se zkontroluje jestli jde o přihlášeného uživatele -> pokud ano -> zobrazí obsah pro daného uživatele
  // pokud nic neprojde, tak se jedná o nepřihlášeného uživatele a dle toho se zobrazí daný obsah
  mounted() {
    const token = localStorage.getItem('x-access-token')
    const currentUsername = localStorage.getItem('username')
    if (token) {
      userService.getAdminBoard().then((response) => {
        this.content = response.data
        const admins = response.data
        // zjišťuje jestli se jedná o admina, pokud ho nalezne vrací true, pokud ne vrací false
        this.isAdmin = admins.some((admin) => admin.username === currentUsername)
        this.isLoggedIn = true
        this.loadEventsFromBackend()
        this.fetchUsers()
        this.loadUserPreferences()

        if (!this.isAdmin) {
          return userService
            .getUserBoard()
            .then((response) => {
              if (response) {
                this.content = response.data
              }
            })
            .catch((error) => {
              this.isAdmin = false
              this.isLoggedIn = false
              console.error('Error:', error.response?.data || error.message)
              this.content = error.response?.data || error.message || 'An error occurred.'
            })
        }
      })
    } else {
      userService.getPublicContent().then(
        (response) => {
          this.content = response.data
        },
        (error) => {
          this.content =
            (error.response && error.response.data) || error.messsage || error.toString()
        }
      )
    }
  }
}
</script>

<template>
  <article v-if="!isLoggedIn">
    <h3>{{ content }}</h3>
  </article>
  <article v-if="isLoggedIn">
    <h1>Kalendář</h1>
    <h4>
      Výběr opakování směn:
      <button
        :class="['service-button', { active: activeButton === 1 }]"
        @click="setActiveButton(1)"
      >
        Jednorázově
      </button>
      <button
        :class="['service-button', { active: activeButton === 2 }]"
        @click="setActiveButton(2)"
      >
        Měsíčně
      </button>
    </h4>
    <h4 v-if="selectedUserId && isAdmin">
      Zakázat zapisovat služby na 2 dny po sobě jdoucí?
      <label class="switch">
        <input type="checkbox" v-model="adjacent" @change="toggleAdjacentRule" />
        <span class="slider round"></span>
      </label>
    </h4>
    <h4 v-else-if="!isAdmin">
      Zakázat zapisovat služby na 2 dny po sobě jdoucí?
      <label class="switch">
        <input type="checkbox" v-model="adjacent" @change="toggleAdjacentRule" />
        <span class="slider round"></span>
      </label>
    </h4>
    <h4 v-if="selectedUserId && isAdmin">
      Zakázat zapisovat služby na více než 2 dny víkendů/svátků měsíčně?
      <label class="switch">
        <input type="checkbox" v-model="holiday" @change="toggleHolidayRule" />
        <span class="slider round"></span>
      </label>
    </h4>
    <h4 v-else-if="!isAdmin">
      Zakázat zapisovat služby na více než 2 dny víkendů/svátků měsíčně?
      <label class="switch">
        <input type="checkbox" v-model="holiday" @change="toggleHolidayRule" />
        <span class="slider round"></span>
      </label>
    </h4>
    <h3 v-if="isAdmin">
      Výběr uživatele:
      <select id="user-dropdown" v-model="selectedUserId" @change="loadUserEvents">
        <option value="" disabled>Vyberte uživatele</option>
        <option v-for="user in users" :key="user._id" :value="user._id">
          {{ user.username }}
        </option>
      </select>
    </h3>
    <h3>
      Zobrazit celkový rozvrh:
      <label class="switch">
        <input type="checkbox" v-model="loadAllEvents" @change="toggleEvents" />
        <span class="slider round"></span>
      </label>
    </h3>
    <h3>Počet vašich směn tento měsíc: ({{ currentEvents.length }})</h3>
    <h3>Počet zbývajících pracovních hodin tento měsíc: {{ remainingWorkHours }}</h3>
    <h3>
      Zbývající počet dní ({{ remainingDaysUntilCutoff.remainingDays }}) před uzávěrkou pro zadání
      služeb měsíce ({{ remainingDaysUntilCutoff.month }})
    </h3>
    <button class="fill-button" @click="autoFillEvents">
      Doplnit služby automaticky na tento měsíc
    </button>
    <FullCalendar :options="calendarOptions" />
    <h4>
      Značení barev v kalendáři:
      <ul class="legend">
        <li><span class="taken"></span> Zabraný den</li>
        <li><span class="hday"></span> Státní svátek</li>
        <li><span class="your-event"></span> Vaše směny</li>
        <li><span class="your-event-recurring"></span> Vaše opakující se směny</li>
        <li><span class="colleague-event"></span> Směny kolegů</li>
      </ul>
    </h4>
  </article>
</template>
