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

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

export const schema = /* GraphQL */ `
  scalar JSON

  enum VoiceExperienceStatus {
    UPLOADED
    TRANSCRIBING
    TRANSCRIBED
    ANALYZING
    ANALYZED
    FAILED
  }

  type Place {
    id: ID!
    googlePlaceId: String!
    name: String!
    latitude: Float!
    longitude: Float!
    googlePrimaryType: String
    googleTypes: [String!]!
    experiences: [VoiceExperience!]!
  }

  type User {
    id: ID!
    telegramId: String!
    username: String
    firstName: String
    lastName: String
    photoUrl: String
    languageCode: String
  }

  type VoiceExperience {
    id: ID!
    place: Place!
    user: User
    status: VoiceExperienceStatus!
    durationSeconds: Int!
    audioObjectKey: String!
    transcript: String
    analysis: JSON
    createdAt: String!
  }

  input CreateVoiceExperienceInput {
    googlePlaceId: String!
    googleName: String!
    latitude: Float!
    longitude: Float!
    durationSeconds: Int!
    audioObjectKey: String!
  }

  input NearbyPlacesInput {
    latitude: Float!
    longitude: Float!
    radiusMeters: Int!
  }

  input AuthenticateTelegramInput {
    initData: String!
  }

  input AuthenticateTelegramLoginInput {
    id: Float!
    first_name: String
    last_name: String
    username: String
    photo_url: String
    auth_date: Float!
    hash: String!
  }

  type AuthPayload {
    user: User!
  }

  type TelegramBotLoginPayload {
    token: String!
    botUrl: String!
    expiresAt: String!
  }

  type TelegramBotLoginSession {
    sessionToken: String!
    user: User!
  }

  type Query {
    health: String!
    me: User!
    places: [Place!]!
    nearbyPlaces(input: NearbyPlacesInput!): [Place!]!
    voiceExperiences: [VoiceExperience!]!
  }

  type Mutation {
    startTelegramBotLogin: TelegramBotLoginPayload!
    completeTelegramBotLogin(token: String!): TelegramBotLoginSession!
    authenticateTelegram(input: AuthenticateTelegramInput!): AuthPayload!
    authenticateTelegramLogin(input: AuthenticateTelegramLoginInput!): AuthPayload!
    createVoiceExperience(input: CreateVoiceExperienceInput!): VoiceExperience!
  }
`;

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;
      await requireTelegramUser(graphqlContext);
      return listPlaces();
    },
    nearbyPlaces: async (
      _: unknown,
      args: { input: Parameters<typeof listNearbyPlaces>[0] },
      context: unknown,
    ) => {
      const graphqlContext = context as GraphqlContext;
      await requireTelegramUser(graphqlContext);
      return listNearbyPlaces(args.input);
    },
    voiceExperiences: async (_: unknown, __: unknown, context: unknown) => {
      const graphqlContext = context as GraphqlContext;
      await requireTelegramUser(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);
    },
  },
};
