import React, { useEffect, useState } from "react";

import { ApiClient, ApiLoadFailedError } from "../api";
import ErrorScreen from "../containers/ErrorScreen";
import { ContribComponentList, ContribComponentMap } from "../types";
import { getCookie } from "../util/get_cookie";

export interface ApiContextProviderProps {
  api:
    | ApiClient
    | string
    | URL
    | {
        schema: string | URL;
        server?: string | URL;
        prefix?: string;
      };
  contrib?: ContribComponentMap | ContribComponentList;
}

const ApiContext = React.createContext<ApiClient>(undefined as unknown as ApiClient);
export const useApi = () => React.useContext(ApiContext);

export const ApiContextProvider: React.FC<React.PropsWithChildren<ApiContextProviderProps>> = ({
  children,
  api: init,
  contrib,
}) => {
  const [api, setApi] = useState<ApiClient>();
  const [error, setError] = useState<ApiLoadFailedError | null>(null);

  useEffect(() => {
    if (api == null) {
      (init instanceof ApiClient
        ? Promise.resolve(init)
        : typeof init === "string" || init instanceof URL
          ? ApiClient.load(init)
          : ApiClient.load(init.schema, init.server)
      )
        .then(async (api) => {
          if (contrib) {
            const contribArray = Array.isArray(contrib)
              ? contrib
              : Object.entries(contrib).map(([tag, contrib]) => ({ tag, contrib }));

            for (const [i, { tag, contrib: hit }] of contribArray.entries()) {
              const operations = api.contrib[tag];
              if (operations) {
                for (const operation of Object.values(operations)) {
                  operation.component = {
                    ...hit,
                    component: hit.component?.() ?? ((() => null) as React.FC),
                  };
                  operation.order = i;
                }
              }
            }
          }

          setApi(api);

          if (getCookie("csrftoken") == null) {
            await api.operations["bananas.csrf:list"].call();
          }
        })
        .catch((error) => setError(error));
    }
  }, [api]);

  if (error != null) {
    return <ErrorScreen error={error} />;
  }

  return <ApiContext.Provider value={api!}>{children}</ApiContext.Provider>;
};

export default ApiContext;
