import { createContext, useContext, useEffect, useState } from 'react';
import { useCache } from './cache';
import type { WidgetProps } from './index';

type UseQueryOptions = {
   cache: boolean;
   cacheKey?: string;
};

export function useQuery<Result>(endpoint: string | undefined, options: UseQueryOptions = { cache: true }) {
   const [cache, setCache] = useCache();

   const [result, setResult] = useState<Result | undefined>(undefined);
   const [fetching, setFetching] = useState(false);
   const [error, setError] = useState<Error | undefined>(undefined);

   useEffect(() => {
      let abortController: AbortController | undefined = undefined;

      async function handleQuery() {
         if (typeof endpoint === 'undefined') {
            return;
         }

         const cacheKey = options.cacheKey ?? endpoint;
         if (options.cache && cacheKey in cache._query) {
            setResult(cache._query[cacheKey] as Result);
            return;
         }

         abortController = new AbortController();
         setFetching(true);
         try {
            const url = new URL(endpoint, `${window.location.protocol}//${window.location.host}`);

            const response = await fetch(url.href, { signal: abortController.signal });
            if (!response.ok || response.status !== 200) {
               throw new Error('Failed to fetch sections');
            }

            const data = await response.json();
            setResult(data);
            setError(undefined);
            if (cache) {
               setCache(prev => ({
                  ...prev,
                  _query: {
                     ...prev._query,
                     [cacheKey]: data,
                  },
               }));
            }
         } catch (e: unknown) {
            if (!(e instanceof Error)) {
               e = new Error(String(e));
            }

            if ((e as Error).name === 'AbortError') {
               return;
            }

            setError(e as Error);
            console.error(e);
         } finally {
            setFetching(false);
         }
      }

      void handleQuery();

      return () => {
         if (abortController) {
            abortController.abort();
         }
      };
   }, [endpoint]);

   function clear() {
      setResult(undefined);
      setFetching(false);
      setError(undefined);
   }

   return { data: result, isFetching: fetching, error, clear };
}

type WidgetPropsContextValue = WidgetProps & {
   hide: () => void;
   show: () => void;
};

const WidgetPropsContext = createContext<WidgetPropsContextValue | undefined>(undefined);
export const WidgetPropsProvider = WidgetPropsContext.Provider;
export function useWidgetProps() {
   return useContext(WidgetPropsContext) as WidgetPropsContextValue;
}
