import React, { createContext, FC, useContext, useEffect, useState } from "react";
import { PlanningTableRow, PositionSchema, UserSchema, WorkShiftsPlanningService } from "../../../../../client";
import { useListState } from "@mantine/hooks";
import { dateToString } from "../../../../../types/utils.ts";
import { getDefaultEndDate } from "../utils/utils.tsx";
import { WorkShiftPlanningRow } from "../types/WorkShiftPlanningRow.tsx";
import useUsersList from "../../../hooks/useUsersList.tsx";
import { notifications } from "../../../../../shared/lib/notifications.ts";
import { getDatesBetween } from "../../../../../shared/lib/date.ts";

type WorkShiftsPlanningContextState = {
    dateRange: [Date | null, Date | null];
    setDateRange: React.Dispatch<React.SetStateAction<[Date | null, Date | null]>>;
    tableRows: WorkShiftPlanningRow[];
    onUpdate: (userId: number, date: Date, positions: string[]) => void;
    users: UserSchema[];
    shownUsers: UserSchema[];
    setShownUsers: (showUsersProp: UserSchema[]) => void;
    userPositions: Map<number, PositionSchema[]>;
    range: Date[];
};

const WorkShiftsPlanningContext = createContext<WorkShiftsPlanningContextState | undefined>(
    undefined,
);

const useWorkShiftsPlanningContextState = () => {
    const [tableRows, tableRowsHandlers] = useListState<WorkShiftPlanningRow>([]);
    const [filteredTableRows, filteredTableRowsHandlers] = useListState<WorkShiftPlanningRow>([]);
    const [dateRange, setDateRange] = useState<
        [Date | null, Date | null]
    >([new Date(), getDefaultEndDate()]);

    const { objects: users } = useUsersList();
    const [shownUsers, shownUsersHandlers] = useListState<UserSchema>([]);

    const userPositions = new Map<number, PositionSchema[]>(
        users.map(user => [user.id, user.position ? [user.position] : []]),
    );

    const filterRows = (showUsersProp: UserSchema[], rows?: WorkShiftPlanningRow[]) => {
        shownUsersHandlers.setState(showUsersProp);
        if (!rows) {
            rows = tableRows;
        }
        filteredTableRowsHandlers.setState(
            rows.filter(row => {
                return showUsersProp.findIndex(user => user.id === row.user.id) !== -1;
            }),
        );
    };

    const setShownUsers = (showUsersProp: UserSchema[]) => {
        filterRows(showUsersProp);
    };

    const processShifts = (shifts: PlanningTableRow[]) => {
        const processedRows: WorkShiftPlanningRow[] = [];
        shifts.forEach((row: PlanningTableRow) => {

            const processedRow: WorkShiftPlanningRow = {
                user: row.user,
                ...Object.fromEntries(
                    row.shifts.reduce((acc, workShift) => {
                        return acc.set(
                            dateToString(new Date(workShift.shiftDate)) ?? "",
                            workShift.positions,
                        );
                    }, new Map<string, PositionSchema[]>()),
                ),
            };
            processedRows.push(processedRow);
        });
        tableRowsHandlers.setState(processedRows);

        let shownUsersProp = shownUsers;
        if (shownUsersProp.length === 0) {
            shownUsersProp = processedRows.map(row => row.user);
        }

        users.forEach((user: UserSchema) => {
            if (processedRows.findIndex(row => row.user.id === user.id) === -1) {
                processedRows.push({ user });
            }
        });

        processedRows.sort((a, b) => a.user.id - b.user.id);
        filterRows(shownUsersProp, processedRows);
    };

    const refetch = () => {
        const dateFrom = dateToString(dateRange[0]);
        const dateTo = dateToString(dateRange[1]);
        if (!(dateFrom && dateTo)) return;

        WorkShiftsPlanningService.getWorkShifts({
            requestBody: {
                dateFrom,
                dateTo,
            },
        })
            .then(res => {
                processShifts(res.shifts);
            })
            .catch(err => console.log(err));
    };

    const onUpdate = (userId: number, date: Date, positionKeys: string[]) => {
        const shiftDate = dateToString(date);
        if (!shiftDate) return;

        WorkShiftsPlanningService.updateWorkShift({
            requestBody: {
                userId,
                shiftDate,
                positionKeys,
            },
        })
            .then(({ ok, message }) => {
                if (!ok) {
                    notifications.error({ message });
                }
                refetch();
            })
            .catch(err => console.log(err));
    };

    useEffect(() => {
        refetch();
    }, [dateRange]);

    const getRange = () => {
        const startDate = dateRange[0];
        if (!startDate) return [];
        const endDate = dateRange[1] ?? getDefaultEndDate(new Date(startDate));
        const dayjsDates = getDatesBetween(startDate, endDate);
        return dayjsDates.map(date => date.toDate());
    };

    const range = getRange();

    return {
        dateRange,
        setDateRange,
        tableRows: filteredTableRows,
        onUpdate,
        users,
        shownUsers,
        setShownUsers,
        userPositions,
        range,
    };
};

type WorkShiftsPlanningContextProviderProps = {
    children: React.ReactNode;
};

export const WorkShiftsPlanningContextProvider: FC<WorkShiftsPlanningContextProviderProps> = ({ children }) => {
    const state = useWorkShiftsPlanningContextState();
    return (
        <WorkShiftsPlanningContext.Provider value={state}>
            {children}
        </WorkShiftsPlanningContext.Provider>
    );
};

export const useWorkShiftsPlanningContext = () => {
    const context = useContext(WorkShiftsPlanningContext);
    if (!context) {
        throw new Error(
            "useWorkShiftsPlanningContext must be used within a WorkShiftsPlanningContextProvider",
        );
    }
    return context;
};
