import Fastify from 'fastify';
import mercurius from 'mercurius';

import { config } from './config.js';
import { prisma } from './prisma.js';
import { resolvers, schema } from './graphql/schema.js';
import {
  fetchTelegramPhoto,
  handleTelegramBotWebhook,
} from './auth/telegram-bot-login.js';
import { fetchGooglePlacePhotoMedia } from './google/places.js';

const app = Fastify({
  logger: true,
  bodyLimit: 25 * 1024 * 1024,
});

app.register(mercurius, {
  schema,
  resolvers,
  graphiql: true,
  context: async (request) => {
    const initDataHeader = request.headers['x-telegram-init-data'];
    const loginDataHeader = request.headers['x-telegram-login-data'];
    const sessionTokenHeader = request.headers['x-mapflow-session-token'];
    return {
      telegramInitData: Array.isArray(initDataHeader) ? initDataHeader[0] : initDataHeader,
      telegramLoginData: Array.isArray(loginDataHeader) ? loginDataHeader[0] : loginDataHeader,
      mapflowSessionToken: Array.isArray(sessionTokenHeader) ? sessionTokenHeader[0] : sessionTokenHeader,
    };
  },
});

app.get('/health', async () => ({ ok: true }));

app.get('/telegram/webhook', async () => ({ ok: true }));

app.post('/telegram/webhook', async (request, reply) => {
  const secretHeader = request.headers['x-telegram-bot-api-secret-token'];
  const secretToken = Array.isArray(secretHeader) ? secretHeader[0] : secretHeader;
  await handleTelegramBotWebhook(request.body as never, secretToken);
  return reply.send({ ok: true });
});

app.get('/telegram/photo/:fileId', async (request, reply) => {
  const params = request.params as { fileId: string };
  const photo = await fetchTelegramPhoto(params.fileId);
  reply.header('content-type', photo.contentType);
  reply.header('cache-control', 'public, max-age=86400');
  reply.header('access-control-allow-origin', '*');
  reply.header('cross-origin-resource-policy', 'cross-origin');
  return reply.send(photo.bytes);
});

app.get('/google/place-photo', async (request, reply) => {
  const query = request.query as { name?: string; maxWidthPx?: string };
  if (!query.name) {
    return reply.code(400).send({ error: 'Google photo name is required.' });
  }

  const photo = await fetchGooglePlacePhotoMedia({
    photoName: query.name,
    maxWidthPx: Number(query.maxWidthPx ?? '900'),
  });
  reply.header('content-type', photo.contentType);
  reply.header('cache-control', 'public, max-age=86400');
  reply.header('access-control-allow-origin', '*');
  reply.header('cross-origin-resource-policy', 'cross-origin');
  return reply.send(photo.bytes);
});

app.get('/audio/voice-experiences/:experienceId', async (request, reply) => {
  const params = request.params as { experienceId: string };
  const query = request.query as { token?: string };
  if (!query.token) {
    return reply.code(401).send({ error: 'Audio token is required.' });
  }

  const experience = await prisma.voiceExperience.findUnique({
    where: { id: params.experienceId },
    select: {
      audioAccessToken: true,
      audioContentBase64: true,
      audioMimeType: true,
    },
  });

  if (
    !experience ||
    experience.audioAccessToken !== query.token ||
    !experience.audioContentBase64 ||
    !experience.audioMimeType
  ) {
    return reply.code(404).send({ error: 'Audio was not found.' });
  }

  reply.header('content-type', experience.audioMimeType);
  reply.header('cache-control', 'no-store');
  return reply.send(Buffer.from(experience.audioContentBase64, 'base64'));
});

app.addHook('onClose', async () => {
  await prisma.$disconnect();
});

await app.listen({ host: config.host, port: config.port });
