import { GraphQLJSONObject } from './scalars.js';
import { typeDefs } from './type-defs.js';
import {
  getOrCreateTelegramLoginUser,
  getOrCreateTelegramUser,
  requireAdminTelegramUser,
  requireTelegramUser,
  type TelegramLoginData,
} from '../auth/telegram.js';
import {
  completeTelegramBotLogin,
  createTelegramBotLogin,
} from '../auth/telegram-bot-login.js';
import {
  addFavoritePlace,
  listFavoritePlaces,
  listNearbyPlaces,
  listPlaces,
  listVoiceExperiences,
  recommendPlacesByVoice,
  removeFavoritePlace,
} from './places.js';
import { createVoiceExperience } from './voice-experiences.js';

export type GraphqlContext = {
  telegramInitData?: string;
  telegramLoginData?: string;
  mapflowSessionToken?: string;
};

export const schema = typeDefs;

export const resolvers = {
  JSON: GraphQLJSONObject,
  Query: {
    health: () => 'ok',
    me: async (_: unknown, __: unknown, context: unknown) => {
      const graphqlContext = context as GraphqlContext;
      return requireTelegramUser(graphqlContext);
    },
    places: async (_: unknown, __: unknown, context: unknown) => {
      const graphqlContext = context as GraphqlContext;
      const user = await requireTelegramUser(graphqlContext);
      return listPlaces(user.id);
    },
    favoritePlaces: async (_: unknown, __: unknown, context: unknown) => {
      const graphqlContext = context as GraphqlContext;
      const user = await requireTelegramUser(graphqlContext);
      return listFavoritePlaces(user.id);
    },
    nearbyPlaces: async (
      _: unknown,
      args: { input: Parameters<typeof listNearbyPlaces>[0] },
      context: unknown,
    ) => {
      const graphqlContext = context as GraphqlContext;
      const user = await requireTelegramUser(graphqlContext);
      return listNearbyPlaces(args.input, user.id);
    },
    voiceExperiences: async (_: unknown, __: unknown, context: unknown) => {
      const graphqlContext = context as GraphqlContext;
      await requireAdminTelegramUser(graphqlContext);
      return listVoiceExperiences();
    },
  },
  Mutation: {
    startTelegramBotLogin: async () => createTelegramBotLogin(),
    completeTelegramBotLogin: async (_: unknown, args: { token: string }) =>
      completeTelegramBotLogin(args.token),
    authenticateTelegram: async (
      _: unknown,
      args: { input: { initData: string } },
    ) => ({ user: await getOrCreateTelegramUser(args.input.initData) }),
    authenticateTelegramLogin: async (
      _: unknown,
      args: { input: TelegramLoginData },
    ) => ({ user: await getOrCreateTelegramLoginUser(args.input) }),
    createVoiceExperience: async (
      _: unknown,
      args: { input: Parameters<typeof createVoiceExperience>[0] },
      context: unknown,
    ) => {
      const graphqlContext = context as GraphqlContext;
      const user = await requireTelegramUser(graphqlContext);
      return createVoiceExperience(args.input, user.id);
    },
    recommendPlacesByVoice: async (
      _: unknown,
      args: { input: Parameters<typeof recommendPlacesByVoice>[0] },
      context: unknown,
    ) => {
      const graphqlContext = context as GraphqlContext;
      const user = await requireTelegramUser(graphqlContext);
      return recommendPlacesByVoice(args.input, user.id);
    },
    addFavoritePlace: async (
      _: unknown,
      args: { placeId: string },
      context: unknown,
    ) => {
      const graphqlContext = context as GraphqlContext;
      const user = await requireTelegramUser(graphqlContext);
      return addFavoritePlace(args.placeId, user.id);
    },
    removeFavoritePlace: async (
      _: unknown,
      args: { placeId: string },
      context: unknown,
    ) => {
      const graphqlContext = context as GraphqlContext;
      const user = await requireTelegramUser(graphqlContext);
      return removeFavoritePlace(args.placeId, user.id);
    },
  },
};
