"use client";

import React, {
  createContext,
  useCallback,
  useContext,
  useRef,
  useState,
} from "react";
import io, { Socket } from "socket.io-client";

import { SHIPPING_ORDER_ID_KEY } from "../constants/app";
import { Environments } from "../constants/environment";
import { SocketMessageType } from "../constants/socket";
import useDebounce from "../hooks/useDebounce";
import { getToken } from "../utils/user-session.util";

const WebSocketWrapperContext = createContext<any>({});

export type WebSocketWrapperProps = {
  children: React.ReactNode;
};

export function WebSocketWrapper({ children }: WebSocketWrapperProps) {
  //
  let socket = useRef<Socket>(null);
  const [isSocketConnected, setIsSocketConnected] = useState<boolean>(false);
  const [paymentProcessFailed, setPaymentProcessFailed] =
    useState<boolean>(false);
  const [paymentProcessResponse, setPaymentProcessResponse] =
    useState<any>(undefined);
  const [isPaymentCompleted, setIsPaymentCompleted] = useState<boolean>(false);

  const disconnectSocket = useCallback(() => {
    if (socket.current) {
      socket.current.close();
    }
  }, []);

  const subscribeEventMessage = useCallback(() => {
    if (!socket.current) return;

    socket.current.on(SocketMessageType.ORDER_PAYMENT_URL, function (data) {
      if (!data.status) {
        setPaymentProcessFailed(true);
      }

      setPaymentProcessResponse(data);
    });
    socket.current.on(
      SocketMessageType.SHIPPING_ORDER_PAYMENT_URL,
      function (data) {
        if (!data.status) {
          setPaymentProcessFailed(true);
        }

        setPaymentProcessResponse(data);
      },
    );

    socket.current.on(
      SocketMessageType.ORDER_PAYMENT_COMPLETED,
      function (data) {
        if (data) {
          setIsPaymentCompleted(true);
        }
      },
    );

    socket.current.on(
      SocketMessageType.SHIPMENT_PAYMENT_COMPLETED,
      async function (data) {
        if (data) {
          await setIsPaymentCompleted(true);
          await localStorage?.setItem(SHIPPING_ORDER_ID_KEY, "");
        }
      },
    );
  }, [
    setPaymentProcessFailed,
    setPaymentProcessResponse,
    setIsPaymentCompleted,
  ]);

  const handleConnect = useDebounce(async () => {
    //TODO validate authorized page

    if (socket.current) return;

    socket.current = io(`${Environments.SOCKET.hostname}`, {
      extraHeaders: {
        Authorization: `Bearer ${await getToken()}`,
      },
    });

    socket.current.on("connect", () => {
      console.log("socket", "connected");
      setIsSocketConnected(true);

      subscribeEventMessage();
    });

    socket.current.on("exception", function (data) {
      console.log("event", data);
      setIsSocketConnected(false);
    });

    socket.current.on("disconnect", function () {
      console.log("Disconnected");
      setIsSocketConnected(false);
    });
  }, 50);

  const sendEventMessage = useCallback(
    (
      event: string,
      msg: string | number | { orderId: string | number; payCurrency?: string },
    ) => {
      if (!socket.current) return;

      setPaymentProcessFailed(false);

      socket.current.emit(event, msg, (data: any) => {
        console.log("orderInfo", data);
      });
    },
    [setPaymentProcessFailed],
  );

  const MainData: WebSocketWrapperContextDto = {
    isSocketConnected,
    paymentProcessFailed,
    paymentProcessResponse,
    isPaymentCompleted,
    initConnection: handleConnect,
    sendEventMessage,
    disconnectSocket,
  };

  return (
    <WebSocketWrapperContext.Provider value={MainData}>
      {children}
    </WebSocketWrapperContext.Provider>
  );
}

export const useWebSocketContext = (): WebSocketWrapperContextDto =>
  useContext(WebSocketWrapperContext);

export type WebSocketWrapperContextDto = {
  isSocketConnected: boolean;
  paymentProcessFailed: boolean;
  paymentProcessResponse: any;
  isPaymentCompleted: boolean;
  initConnection: (...args: any) => void;
  sendEventMessage: (
    event: string,
    msg: string | number | { orderId: string | number; payCurrency?: string },
  ) => void;
  disconnectSocket: () => void;
};
