"use client";

import { useState, useEffect, useCallback } from "react";
import { API_BASE_URL } from "@/config/config";

// Default timeout for fetch requests
const FETCH_TIMEOUT = 5000; // 5 seconds

interface UseContextHookOptions<T> {
  // The endpoint path (without API_BASE_URL)
  endpoint: string;

  // The name of the context for logging
  contextName: string;

  // Function to extract available items (e.g., selectionList for menu)
  extractAvailableItems?: (data: T) => string[];

  // Whether to enable periodic refresh
  enablePeriodicRefresh?: boolean;

  // Refresh interval in milliseconds (default: 5 minutes)
  refreshInterval?: number;

  // Custom fetch function for getting data
  customFetch?: () => Promise<T | null>;

  // Custom update function for setting data
  customUpdate?: (newData: T) => Promise<boolean>;

  // Default value to use when data is not available
  defaultValue?: T;
}

interface UseContextHookResult<T> {
  data: T | null;
  availableItems: string[];
  isLoading: boolean;
  error: string | null;
  refresh: () => Promise<void>;
  update: (newData: T) => Promise<boolean>;
}

/**
 * Factory function to create a custom hook for any context type
 */
export function createContextHook<T>(options: UseContextHookOptions<T>) {
  const {
    endpoint,
    contextName,
    extractAvailableItems = () => [],
    enablePeriodicRefresh = false,
    refreshInterval = 5 * 60 * 1000, // 5 minutes
    customFetch,
    customUpdate,
    defaultValue,
  } = options;

  // The API endpoint paths
  const apiEndpoint = `/api${endpoint}`;
  const directEndpoint = `${API_BASE_URL}${endpoint}`;

  /**
   * Fetches data from the context API
   */
  async function fetchData(): Promise<T | null> {
    try {
      // Create an AbortController to handle timeouts
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT);

      const result = await fetch(directEndpoint, {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          "Cache-Control": "no-cache",
        },
        signal: controller.signal,
      });

      clearTimeout(timeoutId);

      if (!result.ok) {
        throw new Error(`HTTP error! Status: ${result.status}`);
      }

      const data = await result.json();

      if (data.status === 200 && data.data) {
        return data.data;
      } else {
        return null;
      }
    } catch (error) {
      return null;
    }
  }

  /**
   * Updates data in the context API
   */
  async function updateData(newData: T): Promise<boolean> {
    try {
      // Create an AbortController to handle timeouts
      const controller = new AbortController();
      const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT);

      const result = await fetch(directEndpoint, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Cache-Control": "no-cache",
        },
        body: JSON.stringify(newData),
        signal: controller.signal,
      });

      clearTimeout(timeoutId);

      if (!result.ok) {
        throw new Error(`HTTP error! Status: ${result.status}`);
      }
      const data = await result.json();
      return data.status === 200;
    } catch (error) {
      return false;
    }
  }

  // Return the custom hook
  return function useContextHook(): UseContextHookResult<T> {
    const [data, setData] = useState<T | null>(defaultValue || null);
    const [availableItems, setAvailableItems] = useState<string[]>([]);
    const [isLoading, setIsLoading] = useState<boolean>(true);
    const [error, setError] = useState<string | null>(null);

    const [selectedClient, setSelectedClient] = useState<any>(null);

    const refreshData = useCallback(async () => {
      try {
        setIsLoading(true);
        setError(null);
        if (customFetch) {
          try {
            const customData = await customFetch();

            if (customData) {
              setData(customData);

              if (extractAvailableItems) {
                const items = extractAvailableItems(customData);
                setAvailableItems(items);
              }

              setIsLoading(false);
              return;
            }
          } catch (customError) {}
        }

        try {
          const response = await fetch(apiEndpoint);
          if (!response.ok) {
            throw new Error(
              `Failed to fetch ${contextName} data: ${response.status}`
            );
          }

          const result = await response.json();
          if (result.status === 200 && result.data) {
            setData(result.data);

            if (extractAvailableItems) {
              const items = extractAvailableItems(result.data);
              setAvailableItems(items);
            }

            setIsLoading(false);
            return;
          }
        } catch (apiError) {
          console.warn(
            `API endpoint failed, falling back to direct fetch:`,
            apiError
          );
        }

        const directData = await fetchData();
        if (directData) {
          setData(directData);
          if (extractAvailableItems) {
            const items = extractAvailableItems(directData);
            setAvailableItems(items);
          }
        } else if (defaultValue) {
          setData(defaultValue);

          if (extractAvailableItems) {
            const items = extractAvailableItems(defaultValue);
            setAvailableItems(items);
          }
        } else {
          setData(null);
          setAvailableItems([]);
        }
      } catch (err) {
        setError(err instanceof Error ? err.message : "Unknown error");
        if (defaultValue) {
          setData(defaultValue);
          if (extractAvailableItems) {
            const items = extractAvailableItems(defaultValue);
            setAvailableItems(items);
          }
        } else {
          setData(null);
          setAvailableItems([]);
        }
      } finally {
        setIsLoading(false);
      }
    }, []);

    // Function to update data
    const update = useCallback(
      async (newData: T): Promise<boolean> => {
        try {
          setIsLoading(true);
          setError(null);

          if (customUpdate) {
            try {
              const success = await customUpdate(newData);

              if (success) {
                await refreshData();
                return true;
              } else {
                setError("Failed to update data with custom update function");
                return false;
              }
            } catch (customError) {}
          }

          try {
            const response = await fetch(apiEndpoint, {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
              },
              body: JSON.stringify(newData),
            });

            if (!response.ok) {
              throw new Error(
                `Failed to update ${contextName} data: ${response.status}`
              );
            }

            const result = await response.json();
            if (result.status === 200) {
              await refreshData(); // Refresh data after update
              return true;
            }
          } catch (apiError) {
            console.warn(
              `API update failed, falling back to direct update:`,
              apiError
            );
            // Fall back to direct update if API update fails
          }

          // Fallback to direct update
          const success = await updateData(newData);

          if (success) {
            // Refresh data to get the updated state
            await refreshData();
            return true;
          } else {
            setError("Failed to update data");
            return false;
          }
        } catch (err) {
          console.error(`Error updating ${contextName} data:`, err);
          setError(err instanceof Error ? err.message : "Unknown error");
          return false;
        } finally {
          setIsLoading(false);
        }
      },
      [refreshData]
    );

    // Fetch data on component mount and set up periodic refresh if enabled
    useEffect(() => {
      refreshData();

      // Set up periodic refresh if enabled
      if (enablePeriodicRefresh) {
        const interval = setInterval(() => {
          refreshData();
        }, refreshInterval);

        return () => clearInterval(interval);
      }
    }, [refreshData, enablePeriodicRefresh, refreshInterval]);

    return {
      data,
      availableItems,
      isLoading,
      error,
      refresh: refreshData,
      update: update,
    };
  };
}
