import { useQuery, useMutation, UseQueryOptions, UseMutationOptions } from '@tanstack/react-query'
import axios, { AxiosError, AxiosRequestConfig } from 'axios'

type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'

interface FetchOptions<TData, TVariables, TError> {
  method?: HttpMethod
  config?: AxiosRequestConfig
  queryOptions?: Omit<UseQueryOptions<TData, TError>, 'queryKey' | 'queryFn'>
  mutationOptions?: Omit<UseMutationOptions<TData, TError, TVariables>, 'mutationFn'>
}

interface QueryResult<TData, TError> {
  data: TData | undefined
  isLoading: boolean
  error: TError | null
  refetch: () => Promise<unknown>
}

interface MutationResult<TData, TVariables, TError> {
  mutate: (variables: TVariables) => void
  isLoading: boolean
  error: TError | null
  data: TData | undefined
}

export function useClientFetch<TData = unknown, TVariables = void, TError = AxiosError>(
  url: string,
  options: FetchOptions<TData, TVariables, TError> = {}
): TVariables extends void
  ? QueryResult<TData, TError>
  : MutationResult<TData, TVariables, TError> {
  const { method = 'GET', config = {}, queryOptions = {}, mutationOptions = {} } = options

  const API_URL = process.env.NEXT_PUBLIC_API_URL
  const fullUrl = url.startsWith('http') ? url : `${API_URL}${url}`

  // всегда вызываем оба хука
  const query = useQuery<TData, TError>({
    queryKey: [url, config.params],
    queryFn: async () => {
      const response = await axios.get(fullUrl, config)
      return response.data
    },
    ...queryOptions,
    // отключаем автоматическое выполнение для мутаций
    enabled: method === 'GET' && queryOptions.enabled !== false,
  })

  const mutation = useMutation<TData, TError, TVariables>({
    mutationFn: async (variables: TVariables) => {
      const response = await axios({
        method: method.toLowerCase(),
        url: fullUrl,
        data: variables,
        ...config,
      })
      return response.data
    },
    ...mutationOptions,
  })

  // возвращаем соответствующий результат в зависимости от метода
  if (method === 'GET') {
    return {
      data: query.data,
      isLoading: query.isLoading,
      error: query.error,
      refetch: query.refetch,
    } as TVariables extends void ? QueryResult<TData, TError> : never
  }

  return {
    mutate: mutation.mutate,
    isLoading: mutation.isPending,
    error: mutation.error,
    data: mutation.data,
  } as TVariables extends void ? never : MutationResult<TData, TVariables, TError>
}

// примеры использования:

/*
// GET запрос
interface UserData {
  id: number
  name: string
  email: string
}

const { data, isLoading, error } = useClientFetch<UserData>('/users/me')

// POST запрос с типизированным payload
interface LoginPayload {
  email: string
  password: string
}

interface LoginResponse {
  token: string
  user: UserData
}

const { mutate, isLoading } = useClientFetch<LoginResponse, LoginPayload>('/auth/login', {
  method: 'POST',
  mutationOptions: {
    onSuccess: (data) => {
      // data типизирован как LoginResponse
    },
    onError: (error) => {
      // error типизирован как AxiosError
    }
  }
})

// использование:
mutate({ email: 'user@example.com', password: '123456' })

// PATCH запрос
interface UpdateUserPayload {
  name?: string
  email?: string
}

const { mutate } = useClientFetch<UserData, UpdateUserPayload>('/users/me', {
  method: 'PATCH'
})

// использование:
mutate({ name: 'New Name' })
*/
