import {
  UseQueryOptions,
  UseMutationOptions
} from "@tanstack/react-query";

// ---------------------------------------------------------------------------
// React Query shared types
// ---------------------------------------------------------------------------

export type QueryConfig<T> = Omit<
  UseQueryOptions<T, Error, T, readonly unknown[]>,
  "queryKey" | "queryFn"
>;

// ---------------------------------------------------------------------------
// Endpoint interfaces
// ---------------------------------------------------------------------------

export interface EndpointWithInput<TInput, TResponse> {
  call(input: TInput): Promise<TResponse>;
  queryKey(input: TInput): readonly unknown[];
  queryOptions(
    config: { input: TInput } & QueryConfig<TResponse>,
  ): UseQueryOptions<TResponse, Error, TResponse, readonly unknown[]>;
}

export interface EndpointWithoutInput<TResponse> {
  call(): Promise<TResponse>;
  queryKey(): readonly unknown[];
  queryOptions(
    config?: QueryConfig<TResponse>,
  ): UseQueryOptions<TResponse, Error, TResponse, readonly unknown[]>;
}

export type Endpoint<TInput, TResponse> = TInput extends void
  ? EndpointWithoutInput<TResponse>
  : EndpointWithInput<TInput, TResponse>;

// ---------------------------------------------------------------------------
// Endpoint builder
// ---------------------------------------------------------------------------

export function endpoint<TInput, TResponse>(
  service: string,
  action: string,
  execute: (input: TInput) => Promise<TResponse>,
) {
  const buildKey = (input?: TInput): readonly unknown[] =>
    input === undefined
      ? [service, action]
      : [service, action, input];

  const call = (input: TInput) => execute(input);

  const queryKey = (input?: TInput) => buildKey(input);

  const queryOptions = (
    config?: { input?: TInput } & QueryConfig<TResponse>,
  ): UseQueryOptions<
    TResponse,
    Error,
    TResponse,
    readonly unknown[]
  > => {
    const { input, ...rest } = config ?? {};

    return {
      ...rest,
      queryKey: buildKey(input),
      queryFn: () => execute(input as TInput),
    };
  };

  const mutationOptions = (
  config?: Omit<
    UseMutationOptions<
      TResponse,
      Error,
      TInput
    >,
    "mutationFn"
  >,
): UseMutationOptions<
  TResponse,
  Error,
  TInput
> => {
  return {
    ...config,
    mutationFn: (
      variables: TInput,
    ): Promise<TResponse> =>
      execute(variables),
  };
};

  return {
    call,
    queryKey,
    queryOptions,
    mutationOptions
  };
}

// ---------------------------------------------------------------------------
// Helper utilities
// ---------------------------------------------------------------------------

export function unwrap<T>(response: { data: T } | T): T {
  if (
    response &&
    typeof response === "object" &&
    "data" in (response as object)
  ) {
    return (response as { data: T }).data;
  }

  return response as T;
}