import React, {
  useEffect,
  useState,
  PropsWithChildren,
  ReactNode,
  useRef,
} from "react";
import { resetAllStores } from "../../hooks/Create";
import { HttpMethod } from "../types";
import useSWR, { preload } from "swr";
import useSWRMutation from "swr/mutation";
import { func } from "prop-types";
import { useToken } from "../../hooks/useToken";
import { ReactFlowProvider, useReactFlow } from "reactflow";
import { createRoomContext } from "@liveblocks/react";
import { useWorkingFlow } from "@/hooks/useWorkingFlow";
import { useWorkingFolder } from "@/hooks/useWorkingFolder";
import { api_endpoint, liveblocks } from "../lib/utils";
import { useCurrentFlow } from "@/hooks/useCurrentFlow";
import { useCurrentFolder } from "@/hooks/useCurrentFolder";
import { useUser } from "@/hooks/useUser";
import { usePages } from "@/hooks/usePages";
import { useRealtimeToken } from "@/hooks/useRealtimeToken";
import { Client, createClient } from "@liveblocks/client";
import { useLiveblocks } from "@/hooks/useLiveblocks";
import { toast, Toaster } from "react-hot-toast";

//theme
import "primereact/resources/themes/lara-light-indigo/theme.css";

//core
import "primereact/resources/primereact.min.css";

//icons
import "primeicons/primeicons.css";
import { useEdgebrix } from "@/hooks/useEdgebrix";

const fetcher = (url: string) =>
  fetch(url)
    .then((res) => res.json())
    .catch((err) => {
      console.log(err);
    });

const Postfetcher = ({
  url,
  token,
  body,
  method = HttpMethod.POST,
}: {
  url: string;
  token: string;
  body: any;
  method: HttpMethod;
}) =>
  fetch(url, {
    method,
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify(body),
  })
    .then((res) => res.json())
    .catch((err) => {
      console.log(err);
    });

const updateFlowData = async (
  folder_id: string,
  flow_id: string,
  data = {},
  rf = {},
  token: string
) => {
  const flow = await apiCall(
    token,
    `/folders/${folder_id}/flows/${flow_id}`,
    {
      flow: {
        ...rf,
      },
      ...data,
    },
    HttpMethod.PUT
  );

  return flow;
};

const getFullFlowData = async (
  folder_id: string,
  flow_id: string,
  token: string
) => {
  const flow = await apiCall(
    token,
    `/folders/${folder_id}/flows/${flow_id}?response_type=full`,
    {},
    HttpMethod.GET
  );

  return flow;
};

const updateFolder = async (token: string, folder_id: string, data: {}) => {
  const r = await apiCall(
    token,
    `/folders/${folder_id}`,
    { ...data },
    HttpMethod.PUT
  );
  return r;
};

const createFolder = async (token: string) => {
  const r = await apiCall(
    token,
    `/folders`,
    { name: "New folder" },
    HttpMethod.POST
  );
  return r;
};

const deleteFlow = async (
  folder_id: string,
  flow_id: string,
  token: string
) => {
  const r = await apiCall(
    token,
    `/folders/${folder_id}/flows/${flow_id}`,
    {},
    HttpMethod.DELETE
  );

  return r;
};

const deleteFolder = async (folder_id: string, token: string) => {
  const r = await apiCall(
    token,
    `/folders/${folder_id}`,
    {},
    HttpMethod.DELETE
  );

  return r;
};

const createFlow = async (folder_id: string, token: string) => {
  const r = await apiCall(
    token,
    `/folders/${folder_id}/flows`,
    {
      name: "Untitled Flow",
    },
    HttpMethod.POST
  );

  return r;
};

const useFolder = (id: string) => {
  const { token, setToken } = useToken(); //useContext(ClientAuthToken);

  const {
    isValidating: isValidating,
    mutate: folderMutate,
    data: folder,
    error: folderError,
    isLoading: isFolderLoading,
  } = useSWR<any>(
    id ? `${api_endpoint}/folders/${id}?token=${token}` : null,
    fetcher,
    {
      keepPreviousData: true,
    }
  );

  return {
    isValidating,
    folderMutate,
    folder,
    folderError,
    isFolderLoading,
  };
};

const preloadRootFolder = (token: string) => {
  // const { token, setToken } = useToken(); //useContext(ClientAuthToken);
  preload(`${api_endpoint}/folders?token=${token}`, fetcher);
};

const useFolders = () => {
  const { token, setToken } = useToken(); //useContext(ClientAuthToken);

  const {
    mutate: mutate,
    data: folders,
    error: foldersError,
    isLoading: isFoldersLoading,
  } = useSWR<any>(`${api_endpoint}/folders?token=${token}`, fetcher, {
    keepPreviousData: true,
  });

  return { mutate, folders, foldersError, isFoldersLoading };
};

const usePlatform = () => {
  const { token, setToken } = useToken(); //useContext(ClientAuthToken);

  const {
    data: platform,
    error: platformError,
    isLoading: isPlatformLoading,
  } = useSWR<any>(`${api_endpoint}/platform?token=${token}`, fetcher, {
    keepPreviousData: true,
  });

  return { platform, platformError, isPlatformLoading };
};

let _flow_id = "";

const useFlow = (flow_id: string | null | undefined) => {
  const { token, setToken } = useToken(); //useContext(ClientAuthToken);

  const {
    data: flow,
    error: isFlowError,
    isLoading: isFlowLoading,
    mutate: flowMutate,
  } = useSWR<any>(
    flow_id
      ? `${api_endpoint}/flows/${flow_id}?token=${token}&response_type=full`
      : null,
    fetcher,
    {
      keepPreviousData: true,
    }
  );

  if (!isFlowLoading && !isFlowError) {
    _flow_id = flow_id as string;
  }

  return { flow, flowMutate, isFlowLoading, isFlowError };
};

const apiCall = async (
  token: string,
  path: string,
  body: any,
  method: HttpMethod
) => {
  const host = api_endpoint;
  try {
    const base_request_params = {
      method: method,
      headers: {
        Authorization: `Bearer ${token}`,
      },
    };

    if (method === HttpMethod.POST || method === HttpMethod.PUT) {
      const request = await fetch(`${host}${path}`, {
        ...base_request_params,
        body: JSON.stringify(body),
      });
      if (request.status !== 200) {
        //throw new Error("Error");
        console.log("Error");
        return null;
      } else {
        console.log("Success");
        const response = await request.json();
        return response;
      }
    }
    if (method === HttpMethod.GET || method === HttpMethod.DELETE) {
      const request = await fetch(`${host}${path}`, base_request_params);
      if (request.status !== 200) {
        //throw new Error("Error");
        return null;
      } else {
        const response = await request.json();
        return response;
      }
    }
  } catch (error) {
    //throw new Error("Error");
    return null;
  }
};
/* 
  This is a actions hook that uses the React Hooks API to fetch actions from the API.
  It returns an array with the actions, isLoading and isError values.
  https://www.robinwieruch.de/react-hooks-fetch-data/
*/
const useActions = () => {
  const { token, setToken } = useToken(); //useContext(ClientAuthToken);

  const [actions, setActions] = useState([]);
  const [isActionsLoading, setIsLoading] = useState(false);
  const [isActionError, setIsError] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setIsError(false);
      setIsLoading(true);

      try {
        const result = await apiCall(
          token ? token : "",
          "/api/edgebrix?tenant_id=8725e476-dc30-4137-b76b-7bc99100311c",
          {},
          HttpMethod.GET
        );
        setActions(result);
      } catch (error) {
        setIsError(true);
      }
      setIsLoading(false);
    };
    fetchData();
  }, [token]);

  return [{ actions, isActionsLoading, isActionError }];
};

const useSearchActions = () => {
  const { token, setToken } = useToken(); //useContext(ClientAuthToken);

  const [query, doQuery] = useState("");
  const {
    data: actions,
    error,
    isLoading,
  } = useSWR<Array<any>>(
    `${api_endpoint}/actions/search?q=${query}&token=${token}`,
    fetcher,
    {
      keepPreviousData: true,
    }
  );

  return { actions, error, isLoading, doQuery };
};

async function getUserRequest(url: string, { arg }: any) {
  console.log("sendRequest", url, arg);
  await fetch(url, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${arg}`,
    },
  });
}

const getUser = () => {
  const {
    data: user,
    error: isUserError,
    trigger: userTrigger,
    isMutating: isUserMutating,
  } = useSWRMutation(`${api_endpoint}/users`, getUserRequest);

  return { user, isUserError, userTrigger, isUserMutating };
};

async function getFlowRequest(url: string, { arg }: any) {
  console.log(
    "getFlowRequest",
    `${url}/${arg.folder_id}/flow/${arg.flow_id}`,
    arg
  );
  const re = await fetch(url, {
    method: "GET",
    headers: {
      Authorization: `Bearer ${arg.token}`,
    },
  });
  const j = await re.json();
  return j;
}

const getFlow = () => {
  const {
    data: flow,
    error: isFlowError,
    trigger: flowTrigger,
    isMutating: isFlowMutating,
  } = useSWRMutation(`${api_endpoint}/folders`, getFlowRequest);

  return { flow, isFlowError, flowTrigger, isFlowMutating };
};

// const useUser = () => {
//   const token = useContext(ClientAuthToken);

//   const {
//     data: user,
//     error: isUserError,
//     isLoading: isUserLoading,
//   } = useSWR<any>(`${api_endpoint}/users?token=${token}`, fetcher, {
//     keepPreviousData: true,
//   });

//   return { user, isUserError, isUserLoading };
// };

type EdgebrixProviderProps = {
  children: Array<ReactNode> | ReactNode;
};

function EdgebrixProvider({
  children,
}: PropsWithChildren<EdgebrixProviderProps>) {
  const { flow, currentFlowId, setCurrentFlowId, setFlow } = useCurrentFlow();
  const { currentFolderId, setCurrentFolderId, setFolder } = useCurrentFolder();
  const { workingFlowId, setWorkingFlowId } = useWorkingFlow();
  const { workingFolderId, setWorkingFolderId } = useWorkingFolder();
  const { setPages } = usePages();

  const { token: _RealTimeToken, setToken: _setRealTimeToken } =
    useRealtimeToken();

  const { user, isLoading, setUser, setIsLoading } = useUser();
  const { token } = useToken();

  const [client, setClient] = useState<Client>();

  const [loading, setLoading] = useState(false);

  const {
    liveblocks: { enterRoom, leaveRoom, room },
  } = useLiveblocks();

  const canvasRef = useRef(null);

  //Load user
  useEffect(() => {
    if (!token) return;

    toast.remove();
    toast.loading("Loading...");
    (async () => {
      setFlow(null);
      setWorkingFolderId("");
      setWorkingFlowId("");
      setFlow(null);
      setFolder(null);
      setIsLoading(true);
      setCurrentFlowId("");
      setCurrentFolderId("");
      const _user = await fetcher(`${api_endpoint}/users?token=${token}`);
      if (_user) {
        if (_user.error) {
          console.log("error", _user.message);
        } else {
          setUser(_user);
        }
      }
    })();
  }, [token]);

  //Load flow based on lasr user state
  useEffect(() => {
    if (!user) return;
    (async () => {
      setCurrentFlowId(user?.state?.flow_id);
      setCurrentFolderId(user?.state?.folder_id);
    })();
  }, [user]);

  useEffect(() => {
    (async () => {
      if (!currentFlowId) return;

      const _flow = await fetcher(
        `${api_endpoint}/flows/${currentFlowId}?response_type=full&token=${token}`
      );

      setFlow(_flow);
      setCurrentFlowId(_flow.flow_id);
      setWorkingFlowId(_flow.flow_id);
      setCurrentFolderId(_flow.folder_id);
    })();
  }, [currentFlowId]);

  useEffect(() => {
    (async () => {
      if (!currentFolderId) return;

      const _folder = await fetcher(
        `${api_endpoint}/folders/${currentFolderId}?token=${token}`
      );

      setFolder(_folder);
      setWorkingFolderId(_folder.id);
      setCurrentFolderId(_folder.id);
      setIsLoading(false);
      toast.dismiss();
    })();
  }, [currentFolderId]);

  return (
    <>
      <div ref={canvasRef}>
        <div>
          <Toaster
            containerStyle={{
              position: "relative",
            }}
          />
        </div>

        {children}
      </div>
    </>
  );
}

export {
  EdgebrixProvider,
  useFolders,
  useFolder,
  useSearchActions,
  useActions,
  createFlow,
  useFlow,
  usePlatform,
  apiCall,
  deleteFolder,
  createFolder,
  preloadRootFolder,
  getFullFlowData,
  updateFlowData,
  updateFolder,
  deleteFlow,
  getUser,
  getFlow,
};
