import { prisma } from "../database";
import type { EventRegistration } from "@/prisma/generated/prisma/client";
import { ValidationError, NotFoundError, ConflictError } from "../errors";
import { eventService } from "./event.service";
import { calculateEventStatus } from "@/lib/eventStatus";
import { sitePreferencesService } from "../preferences/site-preferences.service";

/**
 * Service de gestion des inscriptions aux événements
 */
export class EventRegistrationService {
  /**
   * Inscrit un utilisateur à un événement
   */
  async registerUserToEvent(
    userId: string,
    eventId: string
  ): Promise<EventRegistration> {
    return prisma.eventRegistration.create({
      data: {
        userId,
        eventId,
      },
    });
  }

  /**
   * Désinscrit un utilisateur d'un événement et retire les points attribués
   */
  async unregisterUserFromEvent(
    userId: string,
    eventId: string
  ): Promise<void> {
    // Vérifier que l'utilisateur est bien inscrit avant de retirer les points
    const isRegistered = await this.checkUserRegistration(userId, eventId);
    if (!isRegistered) {
      return; // Pas d'inscription, rien à faire
    }

    // Récupérer les points à retirer depuis les préférences du site
    const sitePreferences =
      await sitePreferencesService.getOrCreateSitePreferences();
    const pointsToRemove = sitePreferences.eventRegistrationPoints || 100;

    // Supprimer l'inscription et retirer les points en parallèle
    await Promise.all([
      prisma.eventRegistration.deleteMany({
        where: {
          userId,
          eventId,
        },
      }),
      prisma.user.update({
        where: { id: userId },
        data: {
          score: {
            decrement: pointsToRemove,
          },
        },
      }),
    ]);
  }

  /**
   * Vérifie si un utilisateur est inscrit à un événement
   */
  async checkUserRegistration(
    userId: string,
    eventId: string
  ): Promise<boolean> {
    const registration = await prisma.eventRegistration.findUnique({
      where: {
        userId_eventId: {
          userId,
          eventId,
        },
      },
    });
    return !!registration;
  }

  /**
   * Récupère l'inscription d'un utilisateur à un événement
   */
  async getUserRegistration(
    userId: string,
    eventId: string
  ): Promise<EventRegistration | null> {
    return prisma.eventRegistration.findUnique({
      where: {
        userId_eventId: {
          userId,
          eventId,
        },
      },
    });
  }

  /**
   * Récupère toutes les inscriptions d'un utilisateur
   */
  async getUserRegistrations(userId: string): Promise<EventRegistration[]> {
    return prisma.eventRegistration.findMany({
      where: {
        userId,
      },
    });
  }

  /**
   * Récupère le nombre d'inscriptions pour un événement
   */
  async getEventRegistrationsCount(eventId: string): Promise<number> {
    const count = await prisma.eventRegistration.count({
      where: {
        eventId,
      },
    });
    return count;
  }

  /**
   * Récupère tous les inscrits d'un événement avec leurs informations
   */
  async getEventRegistrations(eventId: string) {
    return prisma.eventRegistration.findMany({
      where: {
        eventId,
      },
      include: {
        user: {
          select: {
            id: true,
            username: true,
            avatar: true,
            score: true,
            level: true,
            hp: true,
            maxHp: true,
            xp: true,
            maxXp: true,
          },
        },
      },
      orderBy: {
        createdAt: "asc",
      },
    });
  }

  /**
   * Valide et inscrit un utilisateur à un événement avec toutes les règles métier
   */
  async validateAndRegisterUser(
    userId: string,
    eventId: string
  ): Promise<EventRegistration> {
    // Vérifier que l'événement existe
    const event = await eventService.getEventById(eventId);
    if (!event) {
      throw new NotFoundError("Événement");
    }

    // Vérifier que l'événement est à venir
    const eventStatus = calculateEventStatus(event.date);
    if (eventStatus !== "UPCOMING") {
      throw new ValidationError(
        "Vous ne pouvez vous inscrire qu'aux événements à venir"
      );
    }

    // Vérifier si l'utilisateur est déjà inscrit
    const isRegistered = await this.checkUserRegistration(userId, eventId);
    if (isRegistered) {
      throw new ConflictError("Vous êtes déjà inscrit à cet événement");
    }

    // Récupérer les points à attribuer depuis les préférences du site
    const sitePreferences =
      await sitePreferencesService.getOrCreateSitePreferences();
    const pointsToAward = sitePreferences.eventRegistrationPoints || 100;

    // Créer l'inscription et attribuer les points en parallèle
    const [registration] = await Promise.all([
      this.registerUserToEvent(userId, eventId),
      prisma.user.update({
        where: { id: userId },
        data: {
          score: {
            increment: pointsToAward,
          },
        },
      }),
    ]);

    return registration;
  }
}

export const eventRegistrationService = new EventRegistrationService();
