import { CustomFeatures, CustomGuildInfo } from '../config/types';
import { QueryClient, useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { UserInfo, getGuild, getGuilds, fetchUserInfo } from '@/api/discord';
import {
  disableFeature,
  enableFeature,
  fetchGuildChannels,
  fetchGuildInfo,
  fetchGuildRoles,
  getFeature,
  updateFeature,
} from '@/api/bot';
import { GuildInfo } from '@/config/types';
import { useAccessToken, useSession } from '@/utils/auth/hooks';

export const client = new QueryClient({
  defaultOptions: {
    mutations: {
      retry: 0,
    },
    queries: {
      refetchOnWindowFocus: false,
      staleTime: Infinity,
      retry: 0,
    },
  },
});

export const Keys = {
  login: ['login'],
  guild_info: (guild: string) => ['guild_info', guild],
  features: (guild: string, feature: string) => ['feature', guild, feature],
  guildRoles: (guild: string) => ['gulid_roles', guild],
  guildChannels: (guild: string) => ['gulid_channel', guild],
};

export const Mutations = {
  updateFeature: (guild: string, id: string) => ['feature', guild, id],
};

export function useGuild(id: string) {
  const accessToken = useAccessToken();

  return useQuery({
    queryKey: ['guild', id],
    queryFn: () => getGuild(accessToken as string, id),
    enabled: accessToken != null
  });
}

export function useGuilds() {
  const accessToken = useAccessToken();

  return useQuery({
    queryKey: ['user_guilds'],
    queryFn: () => getGuilds(accessToken as string),
    enabled: accessToken != null
  });
}

export function useSelfUserQuery() {
  const accessToken = useAccessToken();

  return useQuery<UserInfo>({
    queryKey: ['users', 'me'],
    queryFn: () => fetchUserInfo(accessToken!!),
    enabled: accessToken != null,
    staleTime: Infinity
  });
}

export function useGuildInfoQuery(guild: string) {
  const { status, session } = useSession();

  return useQuery<CustomGuildInfo | null>({
    queryKey: Keys.guild_info(guild),
    queryFn: () => fetchGuildInfo(session!!, guild),
    enabled: status === 'authenticated',
    refetchOnWindowFocus: true,
    retry: false,
    staleTime: 0
  });
}

export function useFeatureQuery<K extends keyof CustomFeatures>(guild: string, feature: K) {
  // eslint-disable-next-line no-unused-vars
  const { status, session } = useSession();

  return useSuspenseQuery({
    queryKey: Keys.features(guild, feature),
    queryFn: () => getFeature(session!!, guild, feature)
  });
}

export type EnableFeatureOptions = { guild: string; feature: string; enabled: boolean };
export function useEnableFeatureMutation() {
  const { session } = useSession();

  return useMutation({
    mutationFn: async ({ enabled, guild, feature }: EnableFeatureOptions) => {
      if (enabled) return enableFeature(session!!, guild, feature);
      return disableFeature(session!!, guild, feature);
    },
    async onSuccess(_, { guild, feature, enabled }) {
      await client.invalidateQueries({queryKey: Keys.features(guild, feature)});
      client.setQueryData<GuildInfo | null>(Keys.guild_info(guild), (prev) => {
        if (prev == null) return null;

        if (enabled) {
          return {
            ...prev,
            enabledFeatures: prev.enabledFeatures.includes(feature)
              ? prev.enabledFeatures
              : [...prev.enabledFeatures, feature],
          };
        } else {
          return {
            ...prev,
            enabledFeatures: prev.enabledFeatures.filter((f) => f !== feature),
          };
        }
      });
    },
  });
}

export type UpdateFeatureOptions = {
  guild: string;
  feature: keyof CustomFeatures;
  options: FormData | string;
};
export function useUpdateFeatureMutation() {
  const { session } = useSession();

  return useMutation({
    mutationFn: (options: UpdateFeatureOptions) =>
      updateFeature(session!!, options.guild, options.feature, options.options),
    onSuccess(updated, options) {
      const key = Keys.features(options.guild, options.feature);

      return client.setQueryData(key, updated);
    },
  });
}

export function useGuildRolesQuery(guild: string) {
  const { session } = useSession();

  return useQuery({
    queryKey: Keys.guildRoles(guild),
    queryFn: () => fetchGuildRoles(session!!, guild)
  });
}

export function useGuildChannelsQuery(guild: string) {
  const { session } = useSession();

  return useQuery({
    queryKey: Keys.guildChannels(guild),
    queryFn: () => fetchGuildChannels(session!!, guild)
  });
}

export function useSelfUser(): UserInfo {
  return useSelfUserQuery().data!!;
}

export function useGuildPreview(guild: string) {
  const query = useGuilds();

  return {
    guild: query.data?.find((g) => g.id === guild),
    query,
  };
}
