import React, { useCallback, useContext, useEffect, useRef, useState } from "react";
import LayoutBuilder from "@/features/layout-builder/layout-builder";
import { AxiosContext } from "@/providers/axios/axios-provider";
import { AxiosError } from "axios";
import OrderType, { OrderItem } from "@/api-queries/orders/order-type";
import { EventVenueType } from "@/api-queries/events/event-type";
import ConfirmationModal from "../Modals/ConfirmationModal";
import OrderChangeSeatsQuery from "@/api-queries/orders/order-change-seats-query";
import ClearOrderChangeSeatsSessionQuery from "@/api-queries/orders/clear-order-change-seats-session-query";
import { OrderChangeSeatsMappingType } from "@/api-queries/orders/order-change-seats-mapping-type";
import CreateOrderSeatChangeTicketLockQuery, { CreateOrderSeatChangeTicketLockQueryParamsType } from "@/api-queries/ticket-locks/create-order-seat-change-ticket-lock-query";
import ReleaseOrderSeatChangeTicketLockQuery, { ReleaseOrderSeatChangeTicketLockQueryParamsType } from "@/api-queries/ticket-locks/release-order-seat-change-ticket-lock-query";
import { FabricObject, TEvent } from "fabric";
import Seat from "@/features/layout-builder/custom-objects/seat/seat";
import GetEventVenueUnrestrictedQuery from "@/api-queries/events/get-event-venue-unrestricted-query";
import LayoutBuilderCanvas from "@/features/layout-builder/layout-builder-canvas";

export type OrderChangeSeatsProps = {
    order: OrderType;
    triggerOrderRefresh: () => void;
};

export type OrderSeatType = {
    seatUniqueId: string;
    ticketCategoryId: number;
};

export default function OrderChangeSeats({ order, triggerOrderRefresh }: OrderChangeSeatsProps) {
    const axios = useContext(AxiosContext);
    const [eventVenueConfig, setEventVenueConfig] = useState<string | null>(null);
    const [layoutBuilderLoading, setLayoutBuilderLoading] = useState<boolean>(false);
    const [venueChanged, setVenueChanged] = useState<boolean>(false);
    const [showLayoutBuilder, setShowLayoutBuilder] = useState(false);
    const [showVenueChangedModal, setShowVenueChangedModal] = useState<boolean>(true);
    const openLayoutBuilder = () => setShowLayoutBuilder(true);
    const closeLayoutBuilder = () => setShowLayoutBuilder(false);
    const initialSelectedSeats = useRef<OrderItem[]>(order.items.filter((orderItem: OrderItem) => orderItem.ticketSeat !== undefined));
    const initialSelectedTicketCategoriesIds = useRef<number[]>([...new Set(order.items.map((orderItem: OrderItem) => orderItem.ticketCategoryId))]);
    const [allocatedSeats, setAllocatedSeats] = useState<OrderSeatType[]>([]);
    const allocatedSeatsRef = useRef<OrderSeatType[]>(allocatedSeats);
    const [deallocatedSeats, setDeallocatedSeats] = useState<OrderSeatType[]>([]);
    const deallocatedSeatsRef = useRef<OrderSeatType[]>(deallocatedSeats);
    const awaitingLockAction = useRef<boolean>(false);
    const disableLocking = useRef<boolean>(false);

    useEffect(() => {
        allocatedSeatsRef.current = allocatedSeats;
    }, [allocatedSeats]);

    useEffect(() => {
        deallocatedSeatsRef.current = deallocatedSeats;
    }, [deallocatedSeats]);

    useEffect(() => {
        setLayoutBuilderLoading(true);

        (
            async () => {
                try {
                    const eventVenue: EventVenueType = await (new GetEventVenueUnrestrictedQuery(axios)).execute({ eventId: order.event.id });
                    setEventVenueConfig(eventVenue.config);
                } catch (e) {
                    if (e instanceof AxiosError) {
                        alert(e.response?.data?.error ?? e.message);
                    }

                    console.error(e);
                }
            }
        )();

        setLayoutBuilderLoading(false);
    }, []);

    useEffect(() => {
        if (venueChanged) {
            setDeallocatedSeats(initialSelectedSeats.current.map((orderItem: OrderItem) => {
                return {
                    seatUniqueId: orderItem.ticketSeat!,
                    ticketCategoryId: orderItem.ticketCategoryId
                };
            }));
        }
    }, [venueChanged]);

    const handleSelectedSeat = async (seatToHandle: OrderSeatType): Promise<boolean> => {
        if (initialSelectedSeats.current.find((orderItem: OrderItem) => seatToHandle.seatUniqueId === orderItem.ticketSeat)) {
            setDeallocatedSeats((previous: OrderSeatType[]) => {
                return previous.filter((orderSeat: OrderSeatType) => orderSeat.seatUniqueId !== seatToHandle.seatUniqueId);
            });

            return true;
        }

        if (allocatedSeatsRef.current.length === deallocatedSeatsRef.current.length) {
            alert('Cannot allocate more seats than you\'ve deallocated.');
            return false;
        }

        const created: boolean = await createSeatLock({
            eventId: order.event.id,
            orderId: order.id,
            ticketCategoryId: seatToHandle.ticketCategoryId,
            seatUniqueId: seatToHandle.seatUniqueId,
        });

        if (created) {
            setAllocatedSeats((previous: OrderSeatType[]) => {
                return [...previous || [], seatToHandle];
            });
        } else {
            return false;
        }

        return true;
    };

    const handleDeselectedSeat = async (seatToHandle: OrderSeatType): Promise<boolean> => {
        if (initialSelectedSeats.current.find((orderItem: OrderItem) => seatToHandle.seatUniqueId === orderItem.ticketSeat)) {
            setDeallocatedSeats((previous: OrderSeatType[]) => {
                return [...previous || [], seatToHandle];
            });

            return true;
        }

        const released: boolean = await releaseSeatLock({
            eventId: order.event.id,
            orderId: order.id,
            ticketCategoryId: seatToHandle.ticketCategoryId,
            seatUniqueId: seatToHandle.seatUniqueId,
        });

        if (released) {
            setAllocatedSeats((previous: OrderSeatType[]) => {
                return previous.filter((orderSeat: OrderSeatType) => orderSeat.seatUniqueId !== seatToHandle.seatUniqueId);
            });
        } else {
            return false;
        }

        return true;
    };

    const changeSeats = async () => {
        if (!order) {
            return;
        }

        if (allocatedSeatsRef.current.length === 0 || deallocatedSeatsRef.current.length === 0) {
            return;
        }

        disableLocking.current = true;

        let seatsMappings: OrderChangeSeatsMappingType[] = [];

        deallocatedSeatsRef.current.forEach((orderSeat: OrderSeatType) => {
            const alreadyExistingMapping: OrderChangeSeatsMappingType | undefined = seatsMappings.find((seatsMapping: OrderChangeSeatsMappingType) => seatsMapping.ticketCategoryId === orderSeat.ticketCategoryId);

            if (alreadyExistingMapping) {
                alreadyExistingMapping.oldSeatsUniqueIds.push(orderSeat.seatUniqueId);
                return;
            }

            seatsMappings.push({
                ticketCategoryId: orderSeat.ticketCategoryId,
                oldSeatsUniqueIds: [orderSeat.seatUniqueId],
                newSeatsUniqueIds: [],
            });
        });

        allocatedSeatsRef.current.forEach((orderSeat: OrderSeatType) => {
            const seatsMapping: OrderChangeSeatsMappingType | undefined = seatsMappings.find((seatsMapping: OrderChangeSeatsMappingType) => seatsMapping.ticketCategoryId === orderSeat.ticketCategoryId);

            if (seatsMapping) {
                seatsMapping.newSeatsUniqueIds.push(orderSeat.seatUniqueId);
            }
        });

        try {
            await (new OrderChangeSeatsQuery(axios)).execute({
                orderId: order.id,
                venueChanged,
                seatsMappings,
            });
        } catch (e: any) {
            if (e.response && !e.response.data.success) {
                alert(e.response.data.errorMessage);
            } else {
                console.error(e);
            }
        }

        disableLocking.current = false;
    };

    const clearSession = async (sendReq: boolean) => {
        if (!order) {
            setVenueChanged(false);
            setShowVenueChangedModal(true);
            setAllocatedSeats([]);
            setDeallocatedSeats([]);
            return;
        }

        if (allocatedSeats.length === 0) {
            setVenueChanged(false);
            setShowVenueChangedModal(true);
            setDeallocatedSeats([]);
            return;
        }

        disableLocking.current = true;

        if (sendReq) {
            const seatsUniqueIds: string[] = allocatedSeatsRef.current.map((orderSeat: OrderSeatType) => orderSeat.seatUniqueId);

            try {
                await (new ClearOrderChangeSeatsSessionQuery(axios)).execute({
                    orderId: order.id,
                    seatsToClearUniqueIds: seatsUniqueIds
                });
            } catch (e) {
                console.error(e);
            }
        }

        setVenueChanged(false);
        setShowVenueChangedModal(true);
        setAllocatedSeats([]);
        setDeallocatedSeats([]);

        disableLocking.current = false;
    };

    const createSeatLock = useCallback(
        async (data: CreateOrderSeatChangeTicketLockQueryParamsType): Promise<boolean> => {
            awaitingLockAction.current = true;

            try {
                await (new CreateOrderSeatChangeTicketLockQuery(axios)).execute(data);
            } catch (e: any) {
                awaitingLockAction.current = false;

                if (e.response && !e.response.data.success) {
                    alert(e.response.data.errorMessage);
                } else {
                    console.error(e);
                }

                return false;
            }

            awaitingLockAction.current = false;

            return true;
        }, []
    );

    const releaseSeatLock = useCallback(
        async (data: ReleaseOrderSeatChangeTicketLockQueryParamsType): Promise<boolean> => {
            awaitingLockAction.current = true;

            try {
                await (new ReleaseOrderSeatChangeTicketLockQuery(axios)).execute(data);
            } catch (e: any) {
                awaitingLockAction.current = false;

                if (e.response && !e.response.data.success) {
                    alert(e.response.data.errorMessage);
                } else {
                    console.error(e);
                }

                return false;
            }

            awaitingLockAction.current = false;

            return true;
        }, []
    );

    const seatDisableFilter = useCallback((seat: Seat): boolean => {
        const foundTicketCategoryId: number | undefined = initialSelectedTicketCategoriesIds.current.find((ticketCategoryId: number) => seat.get('meta').ticketCategoryId === ticketCategoryId);

        if (foundTicketCategoryId === undefined) {
            return true;
        }

        return false;
    }, []);

    const seatInitialSelectFilter = useCallback((seat: Seat): boolean => {
        const foundInInitial: OrderItem | undefined = initialSelectedSeats.current.find((orderItem: OrderItem) => seat.get('meta').uniqueId === orderItem.ticketSeat!);

        if (foundInInitial !== undefined) {
            return true;
        }

        return false;
    }, []);

    const seatClickHandler = async function (this: LayoutBuilderCanvas, event: Partial<TEvent> & { target: FabricObject; }) {
        if (awaitingLockAction.current || disableLocking.current) {
            return;
        }

        const seat: Seat = event.target as Seat;

        if (seat.disabled) {
            alert('Seat disabled.');
            return;
        }

        const ticketCategoryId: number = seat.get('meta').ticketCategoryId;
        const seatUniqueId: string = seat.get('meta').uniqueId;

        const foundInInitial: OrderItem | undefined = initialSelectedSeats.current.find((orderItem: OrderItem) => seatUniqueId === orderItem.ticketSeat!);

        if (seat.get('meta').taken && foundInInitial === undefined) {
            alert('Seat already taken.');
            return;
        }

        const orderSeat: OrderSeatType = {
            seatUniqueId,
            ticketCategoryId
        };

        if (seat.selected) {
            const succeeded: boolean = await handleDeselectedSeat(orderSeat);
            succeeded && seat.deselect();
        } else {
            const succeeded: boolean = await handleSelectedSeat(orderSeat);

            succeeded && seat.select();
        }
    };

    return <>
        <button
            type='button'
            onClick={() => {
                if (layoutBuilderLoading) {
                    return;
                }

                openLayoutBuilder();
            }}
            className='px-5 py-2.5 bg-dark-orange hover:bg-dark-orange/80 text-white font-medium text-sm rounded-lg transition-colors duration-150 flex gap-2 items-center justify-self-end max-w-fit'
        >
            {layoutBuilderLoading ? `Se încarcă...` : `Schimbă locuri`}
        </button>

        {showLayoutBuilder &&
            <div className="fixed w-screen h-screen bg-white top-0 left-0 z-10">
                <LayoutBuilder
                    initialData={eventVenueConfig ?? undefined}
                    operationMode='seat-selector'
                    onClose={async () => {
                        if (awaitingLockAction.current) {
                            alert('Lock action happening. Cannot close.');

                            return;
                        }

                        await clearSession(false);
                        closeLayoutBuilder();
                    }}
                    onCancel={async () => {
                        if (awaitingLockAction.current) {
                            alert('Lock action happening. Cannot close.');

                            return;
                        }

                        await clearSession(true);
                        closeLayoutBuilder();
                    }}
                    onSave={async () => {
                        if (awaitingLockAction.current) {
                            alert('Lock action happening. Cannot save.');

                            return;
                        }

                        await changeSeats();
                        triggerOrderRefresh();
                    }}
                    canvasEvents={[
                        {
                            eventName: 'seat:clicked',
                            eventHandler: seatClickHandler
                        }
                    ]}
                    canvasPostLoadActions={[
                        {
                            actionName: 'initDisabledSeats',
                            actionArgs: {
                                filterCallback: seatDisableFilter
                            }
                        },
                        {
                            actionName: 'initSeatsClickEvent'
                        },
                        {
                            actionName: 'initInitialSelectedSeats',
                            actionArgs: {
                                filterCallback: seatInitialSelectFilter
                            }
                        }
                    ]}
                >
                    {showVenueChangedModal &&
                        <ConfirmationModal
                            confirmationText='Has the event venue been changed?'
                            onConfirm={() => {
                                setVenueChanged(true);
                                setShowVenueChangedModal(false);
                            }}
                            onReject={() => {
                                setVenueChanged(false);
                                setShowVenueChangedModal(false);
                            }}
                        />
                    }
                </LayoutBuilder>
            </div>
        }
    </>;
}
