<template>
    <div class="card app-calendar-wrapper">
        <div class="row g-0">
            <div class="col-3 border-end text-center" id="app-calendar-sidebar">
                <div class="card-body">
                    <img
                        v-if="user"
                        :src="`${profileImgUrl}profile/${user.profile}`"
                        alt="Profile Image"
                        class="w-px-50 h-px-50 rounded-circle profile-img"
                        @error="$event.target.src = '/img/icons/icon.png'"
                    />
                    <p class="mt-2 fw-bold">
                        {{ user.name }}
                    </p>

                    <CommuterBtn :userId="user.id" :checkedInProject="checkedInProject || {}" ref="workTimeComponentRef" />

                    <MainEventList
                        :categoryList="categoryList"
                        :baseUrl="baseUrl"
                        :birthdayList="birthdayList"
                        :vacationList="vacationList"
                        :birthdayPartyList="birthdayPartyList"
                        :dinnerList="dinnerList"
                        :teaTimeList="teaTimeList"
                        :workShopList="workShopList"
                        @handle-click-vacation="handleClickVacation"
                    />
                </div>
            </div>

            <div class="col app-calendar-content">
                <div class="card shadow-none border-0">
                    <div class="card-body">
                        <full-calendar
                            ref="fullCalendarRef"
                            :events="calendarEvents"
                            :options="calendarOptions"
                            defaultView="dayGridMonth"
                            class="flatpickr-calendar-only"
                        >
                        </full-calendar>
                        <input ref="calendarDatepicker" type="text" class="d-none" />
                    </div>
                </div>
            </div>
        </div>
    </div>

    <EventModal
        v-if="showModal"
        :position="modalPosition"
        :selected-date="selectedDate"
        :base-url="baseUrl"
        :date-events="currentDateEvents"
        @select="handleEventSelect"
        @delete="handleEventDelete"
        @insert="handleEventInsert"
        @close="handleCloseModal"
    />
</template>

<script setup>
    import { inject, onMounted, reactive, ref, watch, nextTick } from 'vue';
    import { fetchHolidays } from '@c/calendar/holiday';
    import { useUserInfoStore } from '@/stores/useUserInfoStore';
    import { useProjectStore } from '@/stores/useProjectStore';
    import { useToastStore } from '@s/toastStore';
    import { useWeatherStore } from '@/stores/useWeatherStore';
    import { useDatePicker } from '@/stores/useDatePicker';
    import { storeToRefs } from 'pinia';
    import router from '@/router';
    import FullCalendar from '@fullcalendar/vue3';
    import dayGridPlugin from '@fullcalendar/daygrid';
    import interactionPlugin from '@fullcalendar/interaction';
    import CommuterBtn from '@c/commuters/CommuterBtn.vue';
    import MainEventList from '@c/main/MainEventList.vue';
    import EventModal from '@c/main/EventModal.vue';
    import $api from '@api';
    import 'flatpickr/dist/flatpickr.min.css';
    import '@/assets/css/app-calendar.css';

    const baseUrl = import.meta.env.VITE_DOMAIN;
    const profileImgUrl = import.meta.env.VITE_SERVER_IMG_URL;
    const user = ref({});
    const userStore = useUserInfoStore();
    const projectStore = useProjectStore();
    const weatherStore = useWeatherStore();
    const datePickerStore = useDatePicker();
    const { dailyWeatherList } = storeToRefs(weatherStore);

    const dayjs = inject('dayjs');
    const fullCalendarRef = ref(null);
    const workTimeComponentRef = ref(null);
    const calendarEvents = ref([]);
    const calendarDatepicker = ref(null);
    //const dailyWeatherList = ref([]);

    const selectedProject = ref(null);
    const checkedInProject = ref(null);

    // 이벤트 모달 관련
    const showModal = ref(false);
    const modalPosition = ref({ x: 0, y: 0 });
    const selectedDate = ref('');

    // 공통 함수
    const $common = inject('common');
    const toastStore = useToastStore();

    // 롱프레스 관련 변수 추가
    const pressTimer = ref(null);
    const longPressDelay = 500; // 0.5초

    /************* category ***************/

    // 이벤트 카테고리 데이터 로딩
    const categoryList = ref([]);
    const fetchCategoryList = async () => {
        const { data } = await $api.get('main/category');
        if (data) categoryList.value = [...data.data.filter(categoryInfo => categoryInfo.CMNCODODR != 0)];
    };

    /************* init ***************/
    const monthBirthdayList = ref([]);
    const monthVacationList = ref([]);
    const monthBirthdayPartyList = ref([]);
    const monthDinnerList = ref([]);
    const monthTeaTimeList = ref([]);
    const monthWorkShopList = ref([]);

    const birthdayList = ref([]);
    const vacationList = ref([]);
    const birthdayPartyList = ref([]);
    const dinnerList = ref([]);
    const teaTimeList = ref([]);
    const workShopList = ref([]);

    const currentDateEvents = ref([]);

    // 생일자, 휴가자, 이벤트 일정 조회
    const fetchEventList = async param => {
        const { data } = await $api.get(`main/eventList?${param}`);
        const res = data?.data;

        // 기존의 공휴일 이벤트는 유지
        const holidayEvents = calendarEvents.value.filter(event => event.classNames?.includes('holiday-event'));
        calendarEvents.value = [...holidayEvents, ...dailyWeatherList.value];

        // 생일자
        if (res?.memberBirthdayList?.length) {
            monthBirthdayList.value = [...res.memberBirthdayList];
            res.memberBirthdayList.forEach(member => {
                addEvent($common.dateFormatter(member.MEMBERBTH, 'YMD'), 'birthday', `${member.MEMBERNAM}`);
            });
        }

        // 휴가자
        if (res?.memberVacationList?.length) {
            monthVacationList.value = [...res.memberVacationList];
            res.memberVacationList.forEach(member => {
                addEvent($common.dateFormatter(member.LOCVACUDT, 'YMD'), 'vacation', `${member.MEMBERNAM}`);
            });
        }

        // 초기화
        monthBirthdayPartyList.value = [];
        monthDinnerList.value = [];
        monthTeaTimeList.value = [];
        monthWorkShopList.value = [];

        if (res?.eventList?.length) {
            res.eventList.forEach(item => {
                switch (item.CMNCODVAL) {
                    case 300203:
                        monthBirthdayPartyList.value = [...monthBirthdayPartyList.value, item];
                        addEvent($common.dateFormatter(item.LOCEVTTME, 'YMD'), 'birthdayParty', '생일파티');
                        break;

                    case 300204:
                        monthDinnerList.value = [...monthDinnerList.value, item];
                        addEvent($common.dateFormatter(item.LOCEVTTME, 'YMD'), 'dinner', '회식');
                        break;

                    case 300205:
                        monthTeaTimeList.value = [...monthTeaTimeList.value, item];
                        addEvent($common.dateFormatter(item.LOCEVTTME, 'YMD'), 'teaTime', '티타임');
                        break;

                    case 300206:
                        monthWorkShopList.value = [...monthWorkShopList.value, item];
                        addEvent($common.dateFormatter(item.LOCEVTTME, 'YMD'), 'workshop', '워크샵');
                        break;
                }
            });
        }
    };

    // 달력에 이벤트 데이터 추가
    const addEvent = (date, type, title) => {
        // 생일의 경우 달력의 현재 년도로 변경하여 처리
        if (type === 'birthday') {
            const calendarApi = fullCalendarRef.value?.getApi();

            if (calendarApi) {
                const calendarDate = calendarApi.currentData.currentDate;
                const { year } = $common.formatDateTime(new Date(calendarDate));
                const birthDate = $common.dateFormatter(date, 'MD');
                date = `${year}-${birthDate}`;
            }
        }

        // 같은 날짜와 타입의 이벤트가 이미 있는지 확인
        const existingEvent = calendarEvents.value.find(
            event => $common.dateFormatter(event.start, 'MD') === $common.dateFormatter(date, 'MD') && event.type == type,
        );

        // 없는 경우에만 추가
        if (!existingEvent) {
            calendarEvents.value.push({
                start: date,
                type: type,
                title: title,
                classNames: [`${type}-event`],
            });
        }
    };

    // 해당일 기준 이벤트 리스트 필터링
    const useFilterEventList = (month, day) => {
        // 생일자
        if (monthBirthdayList.value) {
            birthdayList.value = $common.filterTargetByDate(monthBirthdayList.value, 'MEMBERBTH', month, day);
        }

        // 휴가자
        if (monthVacationList.value) {
            vacationList.value = $common.filterTargetByDate(monthVacationList.value, 'LOCVACUDT', month, day);
        }

        // 생일파티
        if (monthBirthdayPartyList.value) {
            birthdayPartyList.value = $common.filterTargetByDate(monthBirthdayPartyList.value, 'LOCEVTTME', month, day);
        }

        // 회식
        if (monthDinnerList.value) {
            dinnerList.value = $common.filterTargetByDate(monthDinnerList.value, 'LOCEVTTME', month, day);
        }

        // 티타임
        if (monthTeaTimeList.value) {
            teaTimeList.value = $common.filterTargetByDate(monthTeaTimeList.value, 'LOCEVTTME', month, day);
        }

        // 워크샵
        if (monthWorkShopList.value) {
            workShopList.value = $common.filterTargetByDate(monthWorkShopList.value, 'LOCEVTTME', month, day);
        }
    };

    // 캘린더 데이터 가져오기
    const fetchData = async () => {
        // FullCalendar API 인스턴스 가져오기
        const calendarApi = fullCalendarRef.value?.getApi();
        if (!calendarApi) return;

        const date = calendarApi.currentData.currentDate;
        const { year, month } = $common.formatDateTime(new Date(date));

        try {
            // 현재 표시 중인 월의 공휴일 정보 가져오기
            const holidayEvents = await fetchHolidays(year, month);
            calendarEvents.value = [...holidayEvents]; // 공휴일 정보로 초기화

            // 이벤트 데이터 가져오기
            const param = new URLSearchParams();
            param.append('year', year);
            param.append('month', month);
            param.append('day', '1'); // 해당 월의 첫날

            await fetchEventList(param);
        } catch (error) {
            console.error('공휴일 정보 로딩 실패:', error);
        }
    };

    // 캘린더 이동 함수 (이전, 다음, 오늘)
    const moveCalendar = async (value = 0) => {
        const calendarApi = fullCalendarRef.value?.getApi();

        if (value === 1) {
            calendarApi.prev(); // 이전 달로 이동
        } else if (value === 2) {
            calendarApi.next(); // 다음 달로 이동
        } else if (value === 3) {
            calendarApi.today(); // 오늘 날짜로 이동
        }

        await fetchData();
    };

    // 날짜 선택 가능 여부를 확인하는 공통 함수
    const isSelectableDate = date => {
        const checkDate = dayjs(date);
        const isWeekend = checkDate.day() === 0 || checkDate.day() === 6; // 주말체크
        // 공휴일 체크
        const isHoliday = calendarEvents.value.some(
            event =>
                event.classNames?.includes('holiday-event') && dayjs(event.start).format('YYYY-MM-DD') === checkDate.format('YYYY-MM-DD'),
        );

        return !isWeekend && !isHoliday;
    };

    // 날짜 셀 클래스 추가 함수
    const getCellClassNames = arg => {
        const cellDate = dayjs(arg.date);
        const classes = [];

        // 선택 불가능한 날짜(과거, 주말, 공휴일)에 동일한 클래스 추가
        if (!isSelectableDate(cellDate)) {
            classes.push('fc-day-sat-sun');
        } else {
            // 선택 가능한 날짜 포인터 클래스 추가
            classes.push('clickable');
        }

        return classes;
    };

    // 날짜 클릭 이벤트 핸들러
    let todayEL = null;
    const handleDateClick = info => {
        if (isSelectableDate(info.date)) {
            if ($common.isToday(info.date)) {
                // 오늘 날짜 클릭 시 클래스 제거하고 요소 저장
                todayEL = info.dayEl;
                todayEL.classList.remove('fc-day-today');
            } else if (todayEL) {
                // 다른 날짜 클릭 시 저장된 오늘 요소에 클래스 다시 추가
                todayEL.classList.add('fc-day-today');
                todayEL = null;
            }
        }
        const { month, day } = $common.formatDateTime(new Date(info.dateStr));
        useFilterEventList(month, day);
    };

    // 오늘 날짜 노란색 배경 복구
    const colorToday = e => {
        if (todayEL != null && !todayEL.classList.contains('fc-day-today')) todayEL.classList.add('fc-day-today');
    };

    // 이벤트 모달 핸들러
    const handleMouseDown = (date, jsEvent) => {
        if (showModal.value) showModal.value = false;

        // 해당 날짜의 이벤트 필터링
        const dateEvents = calendarEvents.value.filter(
            event => $common.dateFormatter(event.start, 'YMD') === $common.dateFormatter(date, 'YMD'),
        );

        pressTimer.value = setTimeout(() => {
            modalPosition.value = {
                x: jsEvent.clientX,
                y: jsEvent.clientY,
            };

            selectedDate.value = date;
            currentDateEvents.value = dateEvents;
            showModal.value = true;
            pressTimer.value = null;
        }, longPressDelay);
    };

    // 이벤트 모달 외부 클릭 시 닫힘
    const handleMouseUp = () => {
        if (pressTimer.value) {
            clearTimeout(pressTimer.value);
            pressTimer.value = null;
        }
    };

    // 이벤트 삭제 api
    const toggleEvent = async (date, code, title) => {
        const { data } = await $api.post('main/toggleEvent', {
            date: date,
            code: code,
            title: title,
        });

        if (data?.code === 200) toastStore.onToast(data.message);

        const { year, month, day } = $common.formatDateTime(new Date(date));
        const param = new URLSearchParams();
        param.append('year', year);
        param.append('month', month);
        param.append('day', day);

        await fetchEventList(param);
        useFilterEventList(month, day);
    };

    // 이벤트 추가 api
    const insertEvent = async (date, code, title, place, time) => {
        const dateTime = $common.dateFormatter(`${date} ${time}`);
        const { data } = await $api.post('main/inserEvent', {
            date: dateTime,
            code: code,
            title: title,
            place: place,
        });

        if (data?.code === 200) toastStore.onToast(data.message);

        const { year, month, day } = $common.formatDateTime(new Date(date));
        const param = new URLSearchParams();
        param.append('year', year);
        param.append('month', month);
        param.append('day', day);

        await fetchEventList(param);
        useFilterEventList(month, day);
    };

    // 이벤트 선택 핸들러
    const handleEventSelect = data => {
        toggleEvent(data.date, data.code, data.title);
        showModal.value = false;
    };

    // 이벤트 추가 핸들러
    const handleEventInsert = data => {
        insertEvent(data.date, data.code, data.title, data.place, data.time);
        showModal.value = false;
    };

    // 이벤트 삭제 핸들러
    const handleEventDelete = data => {
        toggleEvent(data.date, data.code, data.title);
        showModal.value = false;
    };

    // 이벤트 모달 닫기
    const handleCloseModal = () => {
        showModal.value = false;
    };

    // 달력 이벤트 아이콘 표시 함수
    const handleEventContent = item => {
        if (!item.event) return null;

        // 공휴일인 경우 텍스트로 표시
        if (item.event.classNames?.includes('holiday-event')) {
            return {
                html: `<div class="holiday-text" style="color: white;">${item.event.title}</div>`,
            };
        }

        // 현재 이벤트의 타입만 확인
        const eventType = item.event.extendedProps.type;
        if (!eventType) return null;

        let iconCode = '';
        switch (eventType) {
            case 'birthday':
                iconCode = '300201';
                break;
            case 'vacation':
                iconCode = '300202';
                break;
            case 'birthdayParty':
                iconCode = '300203';
                break;
            case 'dinner':
                iconCode = '300204';
                break;
            case 'teaTime':
                iconCode = '300205';
                break;
            case 'workshop':
                iconCode = '300206';
                break;
            default:
                return null;
        }

        return {
            html: `<img src="${baseUrl}img/main-category-img/main-${iconCode}.png" class="calendar-event-icon" style="width: 20px; height: 20px; margin: 2px;" />`,
        };
    };

    // 캘린더 옵션 설정
    const calendarOptions = reactive({
        plugins: [dayGridPlugin, interactionPlugin],
        initialView: 'dayGridMonth',
        headerToolbar: {
            left: 'today',
            center: 'title',
            right: 'prev,next',
        },
        locale: 'kr',
        events: calendarEvents,
        eventOrder: 'sortIdx',
        contentHeight: 'auto',
        eventContent: handleEventContent,
        selectable: true,
        selectAllow: selectInfo => isSelectableDate(selectInfo.start),
        dateClick: handleDateClick,
        dayCellDidMount: arg => {
            // 날씨 정보 업데이트
            addWeatherInfo(arg);
            const dateCell = arg.el;

            // 마우스 홀드시 이벤트 모달
            dateCell.addEventListener('mousedown', e => {
                if (!isSelectableDate(arg.date)) return; // 공휴일 제외
                const date = $common.dateFormatter(arg.date, 'YMD');
                handleMouseDown(date, e);
            });
            dateCell.addEventListener('mouseup', handleMouseUp);
            dateCell.addEventListener('mouseleave', handleMouseUp);
        },
        dayCellClassNames: getCellClassNames,
        unselectAuto: true,
        droppable: false,
        eventDisplay: 'block',
        customButtons: {
            prev: {
                text: 'PREV',
                click: () => moveCalendar(1),
            },
            today: {
                text: 'TODAY',
                click: () => moveCalendar(3),
            },
            next: {
                text: 'NEXT',
                click: () => moveCalendar(2),
            },
        },
    });

    // 날짜 정보 업데이트
    const addWeatherInfo = arg => {
        const dateStr = $common.dateFormatter(arg.date, 'YMD');
        // 해당 셀의 날짜와 일치하는 데이터
        const theDayWeatherInfo = dailyWeatherList.value.find(weather => weather.date === dateStr);
        const dayTopEl = arg.el.querySelector('.fc-daygrid-day-top');
        const isWeatherInfoExist = dayTopEl.getElementsByClassName('weather-icon').length > 0; // 중복 방지

        if (theDayWeatherInfo && !isWeatherInfoExist) {
            let weatherIconUrl = `https://openweathermap.org/img/wn/${theDayWeatherInfo.icon}.png`;
            if (theDayWeatherInfo.icon === '01d' || theDayWeatherInfo.icon === '01n') {
                weatherIconUrl = '/img/icons/sunny-custom.png';
            }
            // 날씨 이미지 세팅
            const weatherEl = document.createElement('img');
            weatherEl.src = weatherIconUrl;
            weatherEl.alt = theDayWeatherInfo.description;
            weatherEl.className = 'weather-icon';
            weatherEl.style.width = '28px';
            weatherEl.style.height = '28px';

            // 해당 셀에 이미지 넣기
            dayTopEl.classList.add('align-items-center');
            dayTopEl.prepend(weatherEl); // 이상하게 가장 앞에 넣어야 일자 뒤에 나옴 reverse 옵션이 있는듯
        }
    };

    // 날씨 데이터 변경 감지하여 날씨 정보 업데이트
    watch(dailyWeatherList, async () => {
        await nextTick(); // DOM이 업데이트된 후 실행
        document.querySelectorAll('.fc-daygrid-day').forEach(dayCell => {
            addWeatherInfo({
                el: dayCell,
                date: dayCell.dataset.date,
            });
        });
    });

    const handleWheelEvent = e => {
        handleCloseModal();
    };

    // 선택한 날의 이벤트 중에 휴가자 항목을 누를때 휴가페이지 이동
    const handleClickVacation = () => {
        router.push('/vacation');
    };

    // 달력 뷰 변경 감지 (월 변경 시 데이터 다시 가져오기)
    watch(
        () => fullCalendarRef.value?.getApi().currentData.viewTitle,
        async () => {
            await fetchData();
        },
    );

    // selectbox 프로젝트 선택 변경 사항 감지
    watch(
        () => projectStore.selectedProject,
        newProject => {
            if (newProject) {
                selectedProject.value = newProject.PROJCTSEQ;
                checkedInProject.value = newProject;
            }
        },
    );

    onMounted(async () => {
        await userStore.userInfo();
        user.value = userStore.user;

        // 저장된 선택 프로젝트 가져오기
        const storedProject = projectStore.getSelectedProject();
        if (storedProject) {
            selectedProject.value = storedProject.PROJCTSEQ;
            checkedInProject.value = storedProject;
        }

        // 오늘 기준 데이터 호출
        const { year, month, day } = $common.getToday();
        const param = new URLSearchParams();
        param.append('year', year);
        param.append('month', month);
        param.append('day', day);

        // 이벤트 카테고리 호출
        await fetchCategoryList();
        await fetchEventList(param);
        useFilterEventList(month, day);

        // 스크롤 감지 이벤트 리스너
        window.addEventListener('wheel', handleWheelEvent);
        window.addEventListener('click', colorToday);

        datePickerStore.initDatePicker(fullCalendarRef, async (year, month, options) => {
            // 데이터 다시 불러오기
            await fetchData();
        });
    });
</script>
<style scoped>
    ::v-deep(.fc-h-event) {
        background-color: transparent;
    }

    ::v-deep(.event-modal) {
        padding: 8px;
        border: 1px solid #ddd;
    }

    ::v-deep(.event-icon-select:hover) {
        transform: scale(1.1);
        transition: transform 0.2s;
    }

    /* 이벤트 모달 노출 시 텍스트 선택 방지 */
    ::v-deep(.fc-daygrid-day) {
        user-select: none;
    }

    ::v-deep(.fc-daygrid-day-events) {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        /* align-content: flex-start;
        align-items: center;
        text-align: center !important; */
    }

    /* 공휴일만 가로로 넗게 나오게 */
    ::v-deep(.fc-daygrid-event-harness:has(.holiday-event)) {
        width: 100% !important;
    }
</style>
