import { randomBytes } from 'node:crypto';

import { enqueueVoiceExperience } from '../hatchet/enqueue-voice-experience.js';
import { prisma } from '../prisma.js';
import { enrichPlaceFromGoogle } from './places.js';
import type { Prisma } from '../generated/prisma/client.js';

const forbiddenGeneratedPlaceIdPrefix = 'manual-';

export type CreateVoiceExperienceInput = {
  googlePlaceId: string;
  googleName: string;
  latitude: number;
  longitude: number;
  googlePrimaryType?: string | null;
  googleTypes?: string[];
  durationSeconds: number;
  audioObjectKey: string;
  audioContentBase64: string;
  audioMimeType: string;
  addToFavorites?: boolean | null;
  recordingPayload?: unknown;
  promptHintsShown?: string[];
};

function randomAudioAccessToken() {
  return randomBytes(32).toString('base64url');
}

export async function createVoiceExperience(
  input: CreateVoiceExperienceInput,
  userId: string,
) {
  const googlePlaceId = input.googlePlaceId.trim();
  const googleName = input.googleName.trim();
  if (googlePlaceId === '') {
    throw new Error('Google place id is required.');
  }
  if (googlePlaceId.startsWith(forbiddenGeneratedPlaceIdPrefix)) {
    throw new Error('Voice experience must be linked to a Google place.');
  }
  if (googleName === '') {
    throw new Error('Google place name is required.');
  }
  if (input.audioContentBase64.trim() === '') {
    throw new Error('Voice experience audio is required.');
  }
  if (input.audioMimeType.trim() === '') {
    throw new Error('Voice experience audio MIME type is required.');
  }

  const place = await prisma.place.upsert({
    where: { googlePlaceId },
    create: {
      googlePlaceId,
      name: googleName,
      latitude: input.latitude,
      longitude: input.longitude,
      googlePrimaryType: input.googlePrimaryType ?? null,
      googleTypes: input.googleTypes ?? [],
    },
    update: {
      name: googleName,
      latitude: input.latitude,
      longitude: input.longitude,
      googlePrimaryType: input.googlePrimaryType ?? undefined,
      googleTypes: input.googleTypes ?? undefined,
    },
  });

  const enrichedPlace = await enrichPlaceFromGoogle({
    id: place.id,
    googlePlaceId,
    name: googleName,
    latitude: input.latitude,
    longitude: input.longitude,
  });

  if (input.addToFavorites === true) {
    await prisma.userFavoritePlace.upsert({
      where: { userId_placeId: { userId, placeId: enrichedPlace.id } },
      create: { userId, placeId: enrichedPlace.id },
      update: {},
    });
  }

  const experience = await prisma.voiceExperience.create({
    data: {
      placeId: enrichedPlace.id,
      userId,
      durationSeconds: input.durationSeconds,
      audioObjectKey: input.audioObjectKey,
      audioContentBase64: input.audioContentBase64,
      audioMimeType: input.audioMimeType,
      audioAccessToken: randomAudioAccessToken(),
      recordingPayload: input.recordingPayload as Prisma.InputJsonValue,
      promptHintsShown: input.promptHintsShown ?? [],
      status: 'UPLOADED',
    },
    include: { place: true, user: true },
  });

  await enqueueVoiceExperience({ experienceId: experience.id });

  return experience;
}
