<script setup>
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import { onMounted, ref, computed, onBeforeMount, nextTick, reactive } from 'vue';

import FetchData from 'components/FetchData.vue';
import WorkerTimeHourChip from 'pages/Worker/Card/WorkerTimeHourChip.vue';
import WorkerTimeForm from 'pages/Worker/Card/WorkerTimeForm.vue';
import WorkerTimeReasonForm from 'pages/Worker/Card/WorkerTimeReasonForm.vue';
import WorkerDateLabel from './WorkerDateLabel.vue';
import WorkerTimeControlCalendar from 'pages/Worker/Card/WorkerTimeControlCalendar.vue';
import RightMenu from 'src/components/common/RightMenu.vue';

import useNotify from 'src/composables/useNotify.js';
import axios from 'axios';
import { useAcl } from 'src/composables/useAcl';
import { useWeekdayStore } from 'src/stores/useWeekdayStore';
import { useStateStore } from 'stores/useStateStore';
import { useState } from 'src/composables/useState';
import { dashIfEmpty } from 'src/filters';
import { useVnConfirm } from 'composables/useVnConfirm';
import { useArrayData } from 'composables/useArrayData';
import { toTimeFormat, secondsToHoursMinutes } from 'filters/date.js';
import toDateString from 'filters/toDateString.js';
import moment from 'moment';
import { date } from 'quasar';

const route = useRoute();
const { t, locale } = useI18n();
const { notify } = useNotify();
const _state = useState();
const user = _state.getUser();
const stateStore = useStateStore();
const weekdayStore = useWeekdayStore();
const weekDays = ref([]);
const { openConfirmationModal } = useVnConfirm();
const { getWeekOfYear } = date;
const defaultDate = computed(() => {
    const timestamp = route.query.timestamp;
    return timestamp ? new Date(timestamp * 1000) : Date.vnNew();
});

const workerTimeFormDialogRef = ref(null);
const workerTimeReasonFormDialogRef = ref(null);
const workerHoursRef = ref(null);
const selectedDate = ref(null);
const startOfWeek = ref(null);
const endOfWeek = ref(null);
const selectedWeekNumber = ref(null);
const state = ref(null);
const reason = ref(null);
const canResend = ref(null);
const weekTotalHours = ref(null);
const workerTimeControlMails = ref([]);
const workerTimeFormProps = reactive({
    dated: null,
    entryId: null,
    entryCode: null,
});

// Array utilizado por QCalendar para seleccionar un rango de fechas
const selectedCalendarDates = ref([]);
// Date formateada para bindear al componente QDate
const selectedDateFormatted = ref(toDateString(defaultDate.value));

const arrayData = useArrayData('Worker');
const acl = useAcl();
const selectedDateYear = computed(() => moment(selectedDate.value).isoWeekYear());
const worker = computed(() => arrayData.store?.data);
const canSend = computed(() => acl.hasAcl('WorkerTimeControl', 'sendMail', 'WRITE'));
const canUpdate = computed(() =>
    acl.hasAcl('WorkerTimeControl', 'updateMailState', 'WRITE'),
);
const isHimself = computed(() => user.value.id === Number(route.params.id));

const columns = computed(() => {
    return weekdayStore.getLocales?.map((day, index) => {
        const obj = {
            label: day.locale,
            formattedDate: getHeaderFormattedDate(weekDays.value[index]?.dated),
            name: day.name,
            align: 'center',
            colIndex: index,
            dayData: weekDays.value[index],
        };
        return obj;
    });
});

const getHeaderFormattedDate = (date) => {
    const newDate = new Date(date);
    const day = String(newDate.getDate()).padStart(2, '0');
    const monthName = newDate.toLocaleString(locale.value, { month: 'long' });
    return `${day} ${monthName}`;
};

const formattedWeekTotalHours = computed(() =>
    secondsToHoursMinutes(weekTotalHours.value),
);

const onInputChange = async (date) => {
    if (!date) return;

    const { timestamp, outside } = date.scope;
    const { year, month, day } = timestamp;
    const _date = new Date(year, month - 1, day);
    setDate(_date);

    if (outside) getMailStates(_date);
};

const setDate = async (date) => {
    if (!date) return;
    selectedDate.value = date;
    selectedDate.value.setHours(0, 0, 0, 0);

    const newStartOfWeek = getStartOfWeek(selectedDate.value); // Obtener el día de inicio de la semana a partir de la fecha seleccionada
    const newEndOfWeek = getEndOfWeek(newStartOfWeek); // Obtener el fin de la semana a partir de la fecha de inicio de la semana

    startOfWeek.value = newStartOfWeek;
    endOfWeek.value = newEndOfWeek;
    selectedWeekNumber.value = getWeekOfYear(newStartOfWeek); // Asignar el número de la semana del año

    getWeekDates(newStartOfWeek, newEndOfWeek);

    await nextTick(); // Esperar actualización del DOM y luego fetchear data necesaria
    await fetchHours();
    await fetchWeekData();
};

// Función para obtener el inicio de la semana a partir de una fecha
const getStartOfWeek = (selectedDate) => {
    const dayOfWeek = selectedDate.getDay(); // Obtener el día de la semana de la fecha seleccionada
    const startOfWeek = new Date(selectedDate);
    startOfWeek.setDate(selectedDate.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1)); // Calcular el inicio de la semana
    return startOfWeek;
};

// Función para obtener el fin de la semana a partir del inicio de la semana
const getEndOfWeek = (startOfWeek) => {
    const endOfWeek = new Date(startOfWeek);
    endOfWeek.setHours(23, 59, 59, 59);
    endOfWeek.setDate(startOfWeek.getDate() + 6); // Calcular el fin de la semana sumando 6 días al inicio de la semana
    return endOfWeek;
};

// Función para obtener las fechas de la semana seleccionada
const getWeekDates = (startOfWeek, endOfWeek) => {
    selectedCalendarDates.value = [];
    weekDays.value = []; // Limpiar la información de las fechas seleccionadas previamente
    let currentDate = new Date(startOfWeek);

    while (currentDate <= endOfWeek) {
        // Iterar sobre los días de la semana
        selectedCalendarDates.value.push(toDateString(currentDate)); // Agregar fecha formateada para el array de fechas bindeado al componente QCalendar
        weekDays.value.push({ dated: new Date(currentDate.getTime()) }); // Agregar el día de la semana al array información de días de la semana
        currentDate = new Date(currentDate.setDate(currentDate.getDate() + 1)); // Avanzar al siguiente día
    }
};

const workerHoursFilter = computed(() => ({
    where: {
        and: [{ timed: { gte: startOfWeek.value } }, { timed: { lte: endOfWeek.value } }],
    },
}));

const getWorkedHours = async (from, to) => {
    weekTotalHours.value = null;
    let _weekTotalHours = 0;
    let params = {
        from: from,
        id: route.params.id,
        to: to,
    };

    const { data } = await axios.get(`Workers/${route.params.id}/getWorkedHours`, {
        params,
    });

    const workDays = data;
    const map = new Map();

    for (const workDay of workDays) {
        workDay.dated = new Date(workDay.dated);
        map.set(workDay.dated, workDay);
        _weekTotalHours += workDay.workedHours;
    }

    for (const weekDay of weekDays.value) {
        const workDay = workDays.find((day) => {
            let from = new Date(day.dated);
            from.setHours(0, 0, 0, 0);

            let to = new Date(day.dated);
            to.setHours(23, 59, 59, 59);

            return weekDay.dated >= from && weekDay.dated <= to;
        });

        if (workDay) {
            weekDay.expectedHours = workDay.expectedHours;
            weekDay.workedHours = workDay.workedHours;
        }
    }
    weekTotalHours.value = _weekTotalHours;
};

const getAbsences = async () => {
    const startYear = startOfWeek.value.getFullYear();
    const endYear = endOfWeek.value.getFullYear();
    const defaultParams = { workerFk: route.params.id, businessFk: null };

    const startData = (
        await axios.get('Calendars/absences', {
            params: { ...defaultParams, year: startYear },
        })
    ).data;

    let endData;
    if (startYear !== endYear) {
        endData = (
            await axios.get('Calendars/absences', {
                params: { ...defaultParams, year: endYear },
            })
        ).data;
    }

    const data = {
        holidays: [...(startData?.holidays || []), ...(endData?.holidays || [])],
        absences: [...(startData?.absences || []), ...(endData?.absences || [])],
    };

    if (data) addEvents(data);
};

const addEvents = (data) => {
    const events = {};

    const addEvent = (day, event) => {
        events[new Date(day).getTime()] = event;
    };

    if (data.holidays) {
        data.holidays.forEach((holiday) => {
            const holidayDetail = holiday.detail && holiday.detail.description;
            const holidayType = holiday.type && holiday.type.name;
            const holidayName = holidayDetail || holidayType;

            addEvent(holiday.dated, {
                name: holidayName,
                color: '#ff0',
            });
        });
    }
    if (data.absences) {
        data.absences.forEach((absence) => {
            const type = absence.absenceType;
            addEvent(absence.dated, {
                name: type.name,
                color: type.rgb,
            });
        });
    }

    weekDays.value.forEach((day) => {
        const timestamp = day.dated.getTime();
        if (events[timestamp]) day.event = events[timestamp];
    });
};

const fetchHours = async () => {
    await workerHoursRef.value.fetch();
    await getWorkedHours(startOfWeek.value, endOfWeek.value);
    await getAbsences();
};

const fetchWeekData = async () => {
    const where = {
        year: selectedDateYear.value,
        week: selectedWeekNumber.value,
    };
    try {
        const [{ data: mailData }, { data: countData }] = await Promise.all([
            axios.get(`Workers/${route.params.id}/mail`, {
                params: { filter: { where } },
            }),
            axios.get('WorkerTimeControlMails/count', { params: { where } }),
        ]);

        const mail = mailData[0];

        state.value = mail?.state;
        reason.value = mail?.reason;
        canResend.value = !!countData.count;
    } catch (error) {
        state.value = null;
        if (error?.status != 403) throw error;
    }
};

const setHours = (data) => {
    for (const weekDay of weekDays.value) {
        if (data) {
            let day = weekDay.dated.getDay();
            weekDay.hours = data
                .filter((hour) => new Date(hour.timed).getDay() == day)
                .sort((a, b) => new Date(a.timed) - new Date(b.timed));
        } else weekDay.hours = null;
    }
};

const getFinishTime = () => {
    if (!weekDays.value || weekDays.value.length === 0) return;

    let today = Date.vnNew();
    today.setHours(0, 0, 0, 0);

    let todayInWeek = weekDays.value.find(
        (day) => day.dated.getTime() === today.getTime(),
    );

    if (todayInWeek && todayInWeek.hours && todayInWeek.hours.length) {
        const remainingTime = todayInWeek.workedHours
            ? (todayInWeek.expectedHours - todayInWeek.workedHours) * 1000
            : null;
        const lastKnownEntry = todayInWeek.hours[todayInWeek.hours.length - 1];
        const lastKnownTime = new Date(lastKnownEntry.timed).getTime();
        const finishTimeStamp =
            lastKnownTime && remainingTime ? lastKnownTime + remainingTime : null;

        if (finishTimeStamp) return toTimeFormat(finishTimeStamp) + ' h.';
    }
};

const updateData = async () => {
    await fetchHours();
    await getMailStates(selectedDate.value);
};

const getMailStates = async (date) => {
    const url = `WorkerTimeControls/${route.params.id}/getMailStates`;
    const year = date.getFullYear();
    const month = date.getMonth() + 1;

    const getMonthStates = async (month, year) => {
        return (await axios.get(url, { params: { month, year } })).data;
    };

    const curMonthStates = await getMonthStates(month, year);

    const prevMonthStates = await getMonthStates(
        month === 1 ? 12 : month - 1,
        month === 1 ? year - 1 : year,
    );

    const postMonthStates = await getMonthStates(
        month === 12 ? 1 : month + 1,
        month === 12 ? year + 1 : year,
    );
    workerTimeControlMails.value = [
        ...curMonthStates,
        ...prevMonthStates,
        ...postMonthStates,
    ];
};

const showWorkerTimeForm = (propValue, formType) => {
    const isEditForm = formType === 'edit';

    workerTimeFormProps.entryId = isEditForm ? propValue.id : null;
    workerTimeFormProps.entryCode = isEditForm ? propValue.entryCode : null;
    workerTimeFormProps.dated = isEditForm ? null : propValue;

    workerTimeFormDialogRef.value.show();
};

const showReasonForm = () => {
    workerTimeReasonFormDialogRef.value.show();
};

const updateWorkerTimeControlMail = async (state, reason) => {
    const params = {
        year: selectedDateYear.value,
        week: selectedWeekNumber.value,
        state,
    };
    const workerId = Number(route.params.id);

    if (reason) params.reason = reason;

    await axios.post(`WorkerTimeControls/${workerId}/updateMailState`, params);
    await getMailStates(selectedDate.value);
    await fetchWeekData();
    notify(t('globals.dataSaved'), 'positive');
};

const isSatisfied = async () => {
    await updateWorkerTimeControlMail('CONFIRMED');
};

const isUnsatisfied = async (reason) => {
    if (!reason) {
        notify(t('You must indicate a reason', 'negative'));
        return;
    }
    updateWorkerTimeControlMail('REVISE', reason);
};

const resendEmail = async () => {
    const params = {
        recipient: worker.value?.user?.emailUser?.email,
        week: selectedWeekNumber.value,
        year: selectedDateYear.value,
        workerId: Number(route.params.id),
        state: 'SENDED',
    };
    await axios.post('WorkerTimeControls/weekly-hour-record-email', params);
    await getMailStates(selectedDate.value);
    notify(t('Email sended'), 'positive');
};

onBeforeMount(() => {
    weekdayStore.initStore();
});

onMounted(async () => {
    await setDate(defaultDate.value);
    await getMailStates(selectedDate.value);
    stateStore.rightDrawer = true;
});
</script>

<template>
    <FetchData
        ref="workerHoursRef"
        url="WorkerTimeControls/filter"
        :filter="workerHoursFilter"
        :params="{
            workerFk: route.params.id,
        }"
        @on-fetch="(data) => setHours(data)"
    />
    <Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
        <div>
            <QBtnGroup push class="q-gutter-x-sm" flat>
                <QBtn
                    v-if="canUpdate && state"
                    :label="t('Satisfied')"
                    color="primary"
                    type="submit"
                    :disabled="state == 'CONFIRMED'"
                    @click="isSatisfied()"
                />
                <QBtn
                    v-if="canUpdate && state"
                    :label="t('Not satisfied')"
                    color="primary"
                    type="submit"
                    :disabled="state == 'REVISE'"
                    @click="showReasonForm()"
                    style="margin-left: 1px"
                />
            </QBtnGroup>
            <QBtnGroup push class="q-gutter-x-sm q-ml-none" flat>
                <QBtn
                    v-if="reason && state && canUpdate"
                    :label="t('Reason')"
                    color="primary"
                    type="submit"
                    @click="showReasonForm()"
                />
                <QBtn
                    v-if="canSend && state !== 'CONFIRMED' && canResend"
                    :label="state ? t('Resend') : t('globals.send')"
                    color="primary"
                    type="submit"
                    @click="
                        openConfirmationModal(
                            t('Send time control email'),
                            t('Are you sure you want to send it?'),
                            resendEmail,
                        )
                    "
                >
                    <QTooltip>
                        {{ state ? t('Resend') : t('globals.send') }}
                        {{ t('email of this week to the user') }}
                    </QTooltip>
                </QBtn>
            </QBtnGroup>
        </div>
    </Teleport>
    <RightMenu>
        <template #right-panel>
            <div class="q-pa-md q-mb-md" style="border: 2px solid #222">
                <QCardSection horizontal>
                    <span class="text-weight-bold text-subtitle1 text-center full-width">
                        {{ t('Hours') }}
                    </span>
                </QCardSection>
                <QCardSection class="column items-center" horizontal>
                    <div>
                        <span class="details-label">{{ t('Total semana') }} </span>
                        <span>: {{ formattedWeekTotalHours }}</span>
                    </div>
                    <div>
                        <span class="details-label">{{ t('Termina a las') }}: </span>
                        <span>{{ dashIfEmpty(getFinishTime()) }}</span>
                    </div>
                </QCardSection>
            </div>
            <WorkerTimeControlCalendar
                v-model:model-value="selectedDateFormatted"
                :selected-dates="selectedCalendarDates"
                :active-date="false"
                :worker-time-control-mails="workerTimeControlMails"
                @click-date="onInputChange"
                @on-moved="getMailStates"
            />
        </template>
    </RightMenu>
    <QPage class="column items-center">
        <QTable :columns="columns" :rows="['']" hide-bottom class="full-width">
            <template #header="props">
                <QTr :props="props" no-hover>
                    <QTh
                        v-for="col in props.cols"
                        :key="col.name"
                        :props="props"
                        auto-width
                        style="vertical-align: top"
                    >
                        <div class="column-title-container">
                            <span class="text-primary">{{ t(col.label) }}</span>
                            <span class="q-mb-xs">{{ col.formattedDate }}</span>
                            <WorkerDateLabel
                                v-if="col.dayData?.event"
                                :color="col.dayData.event.color"
                            >
                                <span>
                                    {{ col.dayData.event.name }}
                                </span>
                            </WorkerDateLabel>
                        </div>
                    </QTh>
                </QTr>
            </template>
            <template #body="props">
                <QTr no-hover>
                    <QTd
                        v-for="(day, index) in props.cols"
                        :key="index"
                        :style="{
                            padding: '20px 16px !important',
                            'vertical-align': 'baseline',
                        }"
                    >
                        <div class="full-width column items-center">
                            <WorkerTimeHourChip
                                v-for="(hour, ind) in day.dayData?.hours"
                                :key="ind"
                                :hour="hour.timed"
                                :manual="hour.manual"
                                :direction="hour.direction"
                                :id="hour.id"
                                @on-hour-entry-deleted="updateData()"
                                @show-worker-time-form="
                                    showWorkerTimeForm(
                                        { id: hour.id, entryCode: hour.direction },
                                        'edit',
                                    )
                                "
                                class="hour-chip"
                            />
                        </div>
                    </QTd>
                </QTr>
                <QTr no-hover>
                    <QTd v-for="(day, index) in props.cols" :key="index">
                        <div class="column items-center justify-center">
                            <span class="q-mb-md text-sm text-body1">
                                {{ secondsToHoursMinutes(day.dayData?.workedHours) }}
                            </span>
                            <QBtn
                                icon="add_circle"
                                v-shortcut="'+'"
                                flat
                                color="primary"
                                class="fill-icon cursor-pointer"
                                @click="showWorkerTimeForm(day.dayData?.dated, 'create')"
                            >
                                <QTooltip>{{ t('Add time') }}</QTooltip>
                            </QBtn>
                        </div>
                    </QTd>
                </QTr>
            </template>
        </QTable>
        <QDialog ref="workerTimeFormDialogRef">
            <WorkerTimeForm v-bind="workerTimeFormProps" @on-data-saved="updateData()" />
        </QDialog>
        <QDialog ref="workerTimeReasonFormDialogRef">
            <WorkerTimeReasonForm
                @on-submit="isUnsatisfied($event)"
                :reason="reason"
                :is-himself="isHimself"
            />
        </QDialog>
    </QPage>
</template>

<style scoped lang="scss">
.column-title-container {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    font-size: 14px;
}

.details-label {
    color: var(--vn-label);
}

.hour-chip {
    margin-bottom: 16px;

    &:last-child {
        margin-bottom: 0px;
    }
}
:deep(.q-td) {
    min-width: 170px;
}
</style>

<i18n>
es:
    Hours: Horas
    Total semana: Total semana
    Termina a las: Termina a las
    Add time: Añadir hora
    Reason: Motivo
    Not satisfied: No conforme
    Satisfied: Conforme
    Resend: Reenviar
    email of this week to the user: email de esta semana al usuario
    Email sended: Email enviado
    Send time control email: Enviar email control horario
    Are you sure you want to send it?: ¿Seguro que quieres enviarlo?
    You must indicate a reason: Debes indicar un motivo
</i18n>
