"use server";
import { retrieveAccessToken } from "@/apicalls/mutual/cookies/token";
import {
  DEFAULT_RESPONSE,
  defaultHeaders,
  FetchOptions,
  HttpMethod,
  ApiResponse,
  DEFAULT_TIMEOUT,
} from "./basics";

/**
 * Creates a promise that rejects after a specified timeout
 * @param ms Timeout in milliseconds
 * @param controller AbortController to abort the fetch request
 * @returns A promise that rejects after the timeout
 */
const createTimeoutPromise = (
  ms: number,
  controller: AbortController
): Promise<never> => {
  return new Promise((_, reject) => {
    setTimeout(() => {
      controller.abort();
      reject(new Error(`Request timed out after ${ms}ms`));
    }, ms);
  });
};

/**
 * Prepares a standardized API response
 * @param response The response data
 * @param statusCode HTTP status code
 * @returns Standardized API response
 */
const prepareResponse = <T>(
  response: T,
  statusCode: number
): ApiResponse<T> => {
  try {
    return {
      status: statusCode,
      data: response || ({} as T),
    };
  } catch (error) {
    console.error("Error preparing response:", error);
    return {
      ...DEFAULT_RESPONSE,
      error: "Response parsing error",
    } as ApiResponse<T>;
  }
};

/**
 * Core fetch function with timeout and error handling
 * @param url The URL to fetch
 * @param options Fetch options
 * @param headers Request headers
 * @param payload Request payload
 * @returns API response
 */
async function coreFetch<T>(
  url: string,
  options: FetchOptions = {},
  headers: Record<string, string> = defaultHeaders,
  payload?: any
): Promise<ApiResponse<T>> {
  const { method = "POST", cache = false, timeout = DEFAULT_TIMEOUT } = options;

  try {
    const controller = new AbortController();
    const signal = controller.signal;

    const fetchOptions: RequestInit = {
      method,
      headers,
      cache: cache ? "force-cache" : "no-cache",
      signal,
    };

    // Add body if needed
    if (method !== "GET" && payload) {
      fetchOptions.body = JSON.stringify(
        // Handle special case for updateDataWithToken
        payload.payload ? payload.payload : payload
      );
    }

    // Create timeout promise
    const timeoutPromise = createTimeoutPromise(timeout, controller);

    // Race between fetch and timeout
    const response = (await Promise.race([
      fetch(url, fetchOptions),
      timeoutPromise,
    ])) as Response;

    const responseJson = await response.json();

    return prepareResponse(responseJson, response.status);
  } catch (error) {
    return {
      ...DEFAULT_RESPONSE,
      error: error instanceof Error ? error.message : "Network error",
    } as ApiResponse<T>;
  }
}

/**
 * Fetch data without authentication
 */
async function fetchData<T>(
  endpoint: string,
  payload?: any,
  method: HttpMethod = "POST",
  cache: boolean = false,
  timeout: number = DEFAULT_TIMEOUT
): Promise<ApiResponse<T>> {
  return coreFetch<T>(
    endpoint,
    { method, cache, timeout },
    defaultHeaders,
    payload
  );
}

/**
 * Fetch data with authentication token
 */
async function fetchDataWithToken<T>(
  endpoint: string,
  payload?: any,
  method: HttpMethod = "POST",
  cache: boolean = false,
  timeout: number = DEFAULT_TIMEOUT
): Promise<ApiResponse<T>> {
  const accessToken = (await retrieveAccessToken()) || "";
  const headers = {
    ...defaultHeaders,
    "eys-acs-tkn": accessToken,
  };

  return coreFetch<T>(endpoint, { method, cache, timeout }, headers, payload);
}

/**
 * Update data with authentication token and UUID
 */
async function updateDataWithToken<T>(
  endpoint: string,
  uuid: string,
  payload?: any,
  method: HttpMethod = "POST",
  cache: boolean = false,
  timeout: number = DEFAULT_TIMEOUT
): Promise<ApiResponse<T>> {
  const accessToken = (await retrieveAccessToken()) || "";
  const headers = {
    ...defaultHeaders,
    "eys-acs-tkn": accessToken,
  };

  return coreFetch<T>(
    `${endpoint}/${uuid}`,
    { method, cache, timeout },
    headers,
    payload
  );
}

export { fetchData, fetchDataWithToken, updateDataWithToken };
