import { prisma } from '@/services/core/database';
import { Prisma } from '@prisma/client';
import { Tag } from '@/lib/types';

/**
 * Service pour la gestion des tags
 */
export const tagsService = {
  /**
   * Récupère tous les tags d'un utilisateur avec leur nombre d'utilisations
   */
  async getTags(userId: string): Promise<(Tag & { usage: number })[]> {
    const tags = await prisma.tag.findMany({
      where: { ownerId: userId },
      include: {
        _count: {
          select: {
            taskTags: true,
          },
        },
      },
      orderBy: { name: 'asc' },
    });

    return tags.map((tag) => ({
      id: tag.id,
      name: tag.name,
      color: tag.color,
      isPinned: tag.isPinned,
      usage: tag._count.taskTags,
    }));
  },

  /**
   * Récupère un tag par son ID (vérifie que l'utilisateur en est propriétaire)
   */
  async getTagById(id: string, userId: string): Promise<Tag | null> {
    const tag = await prisma.tag.findFirst({
      where: {
        id,
        ownerId: userId,
      },
    });

    if (!tag) return null;

    return {
      id: tag.id,
      name: tag.name,
      color: tag.color,
      isPinned: tag.isPinned,
    };
  },

  /**
   * Récupère un tag par son nom pour un utilisateur spécifique
   */
  async getTagByName(name: string, userId: string): Promise<Tag | null> {
    const tag = await prisma.tag.findFirst({
      where: {
        name: {
          equals: name,
        },
        ownerId: userId,
      },
    });

    if (!tag) return null;

    return {
      id: tag.id,
      name: tag.name,
      color: tag.color,
      isPinned: tag.isPinned,
    };
  },

  /**
   * Crée un nouveau tag pour un utilisateur
   */
  async createTag(data: {
    name: string;
    color: string;
    isPinned?: boolean;
    userId: string;
  }): Promise<Tag> {
    // Vérifier si le tag existe déjà pour cet utilisateur
    const existing = await this.getTagByName(data.name, data.userId);
    if (existing) {
      throw new Error(`Un tag avec le nom "${data.name}" existe déjà`);
    }

    const tag = await prisma.tag.create({
      data: {
        name: data.name.trim(),
        color: data.color,
        isPinned: data.isPinned || false,
        ownerId: data.userId,
      },
    });

    return {
      id: tag.id,
      name: tag.name,
      color: tag.color,
      isPinned: tag.isPinned,
    };
  },

  /**
   * Met à jour un tag (vérifie que l'utilisateur en est propriétaire)
   */
  async updateTag(
    id: string,
    userId: string,
    data: { name?: string; color?: string; isPinned?: boolean }
  ): Promise<Tag | null> {
    // Vérifier que le tag existe et appartient à l'utilisateur
    const existing = await this.getTagById(id, userId);
    if (!existing) {
      throw new Error(
        `Tag avec l'ID "${id}" non trouvé ou vous n'en êtes pas propriétaire`
      );
    }

    // Si on change le nom, vérifier qu'il n'existe pas déjà pour cet utilisateur
    if (data.name && data.name !== existing.name) {
      const nameExists = await this.getTagByName(data.name, userId);
      if (nameExists && nameExists.id !== id) {
        throw new Error(`Un tag avec le nom "${data.name}" existe déjà`);
      }
    }

    const updateData: Prisma.TagUpdateInput = {};
    if (data.name !== undefined) {
      updateData.name = data.name.trim();
    }
    if (data.color !== undefined) {
      updateData.color = data.color;
    }
    if (data.isPinned !== undefined) {
      updateData.isPinned = data.isPinned;
    }

    if (Object.keys(updateData).length === 0) {
      return existing;
    }

    const tag = await prisma.tag.update({
      where: { id },
      data: updateData,
    });

    return {
      id: tag.id,
      name: tag.name,
      color: tag.color,
      isPinned: tag.isPinned,
    };
  },

  /**
   * Supprime un tag et toutes ses relations (vérifie que l'utilisateur en est propriétaire)
   */
  async deleteTag(id: string, userId: string): Promise<void> {
    // Vérifier que le tag existe et appartient à l'utilisateur
    const existing = await this.getTagById(id, userId);
    if (!existing) {
      throw new Error(
        `Tag avec l'ID "${id}" non trouvé ou vous n'en êtes pas propriétaire`
      );
    }

    // Supprimer d'abord toutes les relations TaskTag
    await prisma.taskTag.deleteMany({
      where: { tagId: id },
    });

    // Puis supprimer le tag lui-même
    await prisma.tag.delete({
      where: { id },
    });
  },

  /**
   * Récupère les tags les plus utilisés pour un utilisateur
   */
  async getPopularTags(
    userId: string,
    limit: number = 10
  ): Promise<Array<Tag & { usage: number }>> {
    // Utiliser une requête SQL brute pour compter les usages
    const tagsWithUsage = await prisma.$queryRaw<
      Array<{
        id: string;
        name: string;
        color: string;
        usage: number;
      }>
    >`
      SELECT t.id, t.name, t.color, COUNT(tt.tagId) as usage
      FROM tags t
      LEFT JOIN task_tags tt ON t.id = tt.tagId
      WHERE t.ownerId = ${userId}
      GROUP BY t.id, t.name, t.color
      ORDER BY usage DESC, t.name ASC
      LIMIT ${limit}
    `;

    return tagsWithUsage.map((tag) => ({
      id: tag.id,
      name: tag.name,
      color: tag.color,
      usage: Number(tag.usage),
    }));
  },

  /**
   * Recherche des tags par nom pour un utilisateur (pour autocomplete)
   */
  async searchTags(
    query: string,
    userId: string,
    limit: number = 10
  ): Promise<Tag[]> {
    const tags = await prisma.tag.findMany({
      where: {
        name: {
          contains: query,
        },
        ownerId: userId,
      },
      orderBy: { name: 'asc' },
      take: limit,
    });

    return tags.map((tag) => ({
      id: tag.id,
      name: tag.name,
      color: tag.color,
      isPinned: tag.isPinned,
    }));
  },

  /**
   * Crée automatiquement des tags manquants à partir d'une liste de noms pour un utilisateur
   */
  async ensureTagsExist(tagNames: string[], userId: string): Promise<Tag[]> {
    const results: Tag[] = [];

    for (const name of tagNames) {
      if (!name.trim()) continue;

      let tag = await this.getTagByName(name.trim(), userId);

      if (!tag) {
        // Générer une couleur aléatoirement
        const colors = [
          '#3B82F6',
          '#EF4444',
          '#10B981',
          '#F59E0B',
          '#8B5CF6',
          '#EC4899',
          '#06B6D4',
          '#84CC16',
        ];
        const randomColor = colors[Math.floor(Math.random() * colors.length)];

        tag = await this.createTag({
          name: name.trim(),
          color: randomColor,
          userId,
        });
      }

      results.push(tag);
    }

    return results;
  },
};
