
import { type DocumentNode } from "graphql";
import {
  type OperationVariables,
  ApolloClient
} from "@apollo/client";
import {GraphqlRequestResult} from "@/types/graphqlFetch/type";
import {getClient} from "@/lib/ApolloClientServer";



/* 定义自己的context类型
import "@apollo/client";
import { HttpLink } from "@apollo/client";
declare module "@apollo/client" {
  interface DefaultContext extends HttpLink.ContextOptions {}
}
*/
// Comprehensive error handling example. https://www.apollographql.com/docs/react/data/error-handling

export type CacheLifePreset =
  | "seconds"
  | "minutes"
  | "hours"
  | "days"
  | "weeks"
  | "max";

export type CacheLifeOption = number | CacheLifePreset;

export function getRevalidateTime(
  life?: CacheLifeOption
): number | false {
  if (!life) return false;
  if (typeof life === "number") return life;

  switch (life) {
    case "seconds":
      return 10;
    case "minutes":
      return 60;
    case "hours":
      return 3600;
    case "days":
      return 86400;
    case "weeks":
      return 604800;
    case "max":
      return false;
    default:
      return false;
  }
}





export function stableStringify(value: unknown): string {
  if (value === null || typeof value !== "object") {
    return JSON.stringify(value);
  }

  if (Array.isArray(value)) {
    return `[${value.map(stableStringify).join(",")}]`;
  }

  const obj = value as Record<string, unknown>;
  return `{${Object.keys(obj)
    .sort()
    .map(
      (key) => `"${key}":${stableStringify(obj[key])}`
    )
    .join(",")}}`;
}




export interface GraphQLRequestOptions {
  tags?: string[];
  life?: CacheLifeOption;
  noCache?: boolean;
  context?: Record<string, unknown>;
  fetchPolicy?:
  | "cache-first"
  | "network-only"
  | "no-cache"
  | "cache-only";
}

export async function graphqlRequest<
  TData = unknown,
  TVariables extends OperationVariables = OperationVariables
>(
  query: DocumentNode,
  variables?: TVariables,
  options?: GraphQLRequestOptions
): Promise<GraphqlRequestResult<TData>> {
  const client = getClient();
  let resData;
  const revalidate = getRevalidateTime(options?.life);
  let queryOption: ApolloClient.QueryOptions<TData> = {
    query,
    variables,
    fetchPolicy: "network-only",
    context: {
      fetchOptions: {
        next: {
          revalidate,
          tags: options?.tags,
        },
      },
    }
  };

  if (options?.noCache) {
    /***
     * client.query的参数是一个对象：
     * {query, variables, context, fetchPolicy, errorPolicy}
     * context.fetchOptions 可以设置nextjs fetch的缓存策略 https://www.apollographql.com/docs/react/integrations/nextjs
     * context: {
        fetchOptions: {
          next: {
            revalidate: 60,      // 对应 life: 'minutes'
            tags: ['posts'],     // 对应 tags 选项
          },
        },
      },
     */
    queryOption = {
      query,
      variables,
      fetchPolicy: "no-cache", // 跳过 apollo client 的缓存，直接调fetch
      context: {
        ...options?.context,
        fetchOptions: {
          cache: 'no-store',   // 👈 告诉 Next.js 不要缓存这个 fetch
        },
      },
    };
  }

  if (options?.context) {
    throw new Error(
      "graphqlRequest: Caching with `context` is unsafe. Use noCache instead."
    );
  }
  try {
      // Promise-based APIs (e.g. client.query, client.mutate) - Errors either reject the promise or are returned in the result as the error field.
      // 如果错误被reject 则会进入catch
      const result: ApolloClient.QueryResult<TData> = await client.query(queryOption);

      resData = result.data || null;
      return {data: resData, error: result.error? result.error.message : '' };
  } catch (error) {
    throw error;
  }
}



export async function graphqlRequestNoCache<
  TData = unknown,
  TVariables extends OperationVariables = OperationVariables
>(
  query: DocumentNode,
  variables?: TVariables,
  options?: Omit<
    GraphQLRequestOptions,
    "noCache" | "tags" | "life"
  >
): Promise<GraphqlRequestResult<TData>> {
  return graphqlRequest<TData, TVariables>(
    query,
    variables,
    {
      ...options,
      noCache: true,
    }
  );
}
