import { prisma } from '@/services/core/database';

export interface Folder {
  id: string;
  name: string;
  userId: string;
  tagId?: string;
  parentId?: string;
  order: number;
  createdAt: Date;
  updatedAt: Date;
  tag?: {
    id: string;
    name: string;
    color: string;
  } | null;
  children?: Folder[];
  notesCount?: number;
}

export interface CreateFolderData {
  name: string;
  userId: string;
  tagId?: string;
  parentId?: string;
  order?: number;
}

export interface UpdateFolderData {
  name?: string;
  tagId?: string;
  parentId?: string;
  order?: number;
}

/**
 * Service pour la gestion des dossiers de notes
 */
export class FoldersService {
  /**
   * Récupère tous les dossiers d'un utilisateur avec leur hiérarchie
   */
  async getFolders(userId: string): Promise<Folder[]> {
    const folders = await prisma.folder.findMany({
      where: { userId },
      include: {
        tag: {
          select: {
            id: true,
            name: true,
            color: true,
          },
        },
        _count: {
          select: {
            notes: true,
          },
        },
      },
      orderBy: { order: 'asc' },
    });

    // Construire l'arborescence
    const folderMap = new Map<string, Folder>();
    const rootFolders: Folder[] = [];

    // Première passe : créer tous les dossiers
    folders.forEach((folder) => {
      const folderData: Folder = {
        id: folder.id,
        name: folder.name,
        userId: folder.userId,
        tagId: folder.tagId || undefined,
        parentId: folder.parentId || undefined,
        order: folder.order,
        createdAt: folder.createdAt,
        updatedAt: folder.updatedAt,
        tag: folder.tag,
        children: [],
        notesCount: folder._count.notes,
      };
      folderMap.set(folder.id, folderData);
    });

    // Deuxième passe : construire la hiérarchie
    folderMap.forEach((folder) => {
      if (folder.parentId) {
        const parent = folderMap.get(folder.parentId);
        if (parent) {
          parent.children!.push(folder);
        }
      } else {
        rootFolders.push(folder);
      }
    });

    return rootFolders;
  }

  /**
   * Récupère un dossier par son ID
   */
  async getFolderById(
    folderId: string,
    userId: string
  ): Promise<Folder | null> {
    const folder = await prisma.folder.findFirst({
      where: {
        id: folderId,
        userId,
      },
      include: {
        tag: {
          select: {
            id: true,
            name: true,
            color: true,
          },
        },
        _count: {
          select: {
            notes: true,
          },
        },
      },
    });

    if (!folder) return null;

    return {
      id: folder.id,
      name: folder.name,
      userId: folder.userId,
      tagId: folder.tagId || undefined,
      parentId: folder.parentId || undefined,
      order: folder.order,
      createdAt: folder.createdAt,
      updatedAt: folder.updatedAt,
      tag: folder.tag,
      notesCount: folder._count.notes,
    };
  }

  /**
   * Crée un nouveau dossier
   */
  async createFolder(data: CreateFolderData): Promise<Folder> {
    const folder = await prisma.folder.create({
      data: {
        name: data.name,
        userId: data.userId,
        tagId: data.tagId,
        parentId: data.parentId,
        order: data.order ?? 0,
      },
      include: {
        tag: {
          select: {
            id: true,
            name: true,
            color: true,
          },
        },
        _count: {
          select: {
            notes: true,
          },
        },
      },
    });

    return {
      id: folder.id,
      name: folder.name,
      userId: folder.userId,
      tagId: folder.tagId || undefined,
      parentId: folder.parentId || undefined,
      order: folder.order,
      createdAt: folder.createdAt,
      updatedAt: folder.updatedAt,
      tag: folder.tag,
      notesCount: folder._count.notes,
    };
  }

  /**
   * Met à jour un dossier existant
   */
  async updateFolder(
    folderId: string,
    userId: string,
    data: UpdateFolderData
  ): Promise<Folder> {
    // Vérifier que le dossier appartient à l'utilisateur
    const existingFolder = await prisma.folder.findFirst({
      where: {
        id: folderId,
        userId,
      },
    });

    if (!existingFolder) {
      throw new Error('Folder not found or access denied');
    }

    // Vérifier qu'on ne crée pas de boucle dans la hiérarchie
    if (data.parentId) {
      const isDescendant = await this.isDescendant(
        folderId,
        data.parentId,
        userId
      );
      if (isDescendant) {
        throw new Error('Cannot move folder to its own descendant');
      }
    }

    const folder = await prisma.folder.update({
      where: { id: folderId },
      data: {
        name: data.name,
        tagId: data.tagId,
        parentId: data.parentId,
        order: data.order,
        updatedAt: new Date(),
      },
      include: {
        tag: {
          select: {
            id: true,
            name: true,
            color: true,
          },
        },
        _count: {
          select: {
            notes: true,
          },
        },
      },
    });

    return {
      id: folder.id,
      name: folder.name,
      userId: folder.userId,
      tagId: folder.tagId || undefined,
      parentId: folder.parentId || undefined,
      order: folder.order,
      createdAt: folder.createdAt,
      updatedAt: folder.updatedAt,
      tag: folder.tag,
      notesCount: folder._count.notes,
    };
  }

  /**
   * Supprime un dossier (et déplace ses notes vers le parent ou null)
   */
  async deleteFolder(folderId: string, userId: string): Promise<void> {
    // Vérifier que le dossier appartient à l'utilisateur
    const existingFolder = await prisma.folder.findFirst({
      where: {
        id: folderId,
        userId,
      },
    });

    if (!existingFolder) {
      throw new Error('Folder not found or access denied');
    }

    // Déplacer les notes vers le parent du dossier supprimé
    await prisma.note.updateMany({
      where: { folderId },
      data: { folderId: existingFolder.parentId },
    });

    // Déplacer les sous-dossiers vers le parent du dossier supprimé
    await prisma.folder.updateMany({
      where: { parentId: folderId },
      data: { parentId: existingFolder.parentId },
    });

    // Supprimer le dossier
    await prisma.folder.delete({
      where: { id: folderId },
    });
  }

  /**
   * Vérifie si un dossier est un descendant d'un autre
   */
  private async isDescendant(
    folderId: string,
    potentialAncestorId: string,
    userId: string
  ): Promise<boolean> {
    if (folderId === potentialAncestorId) return true;

    const folder = await prisma.folder.findFirst({
      where: {
        id: potentialAncestorId,
        userId,
      },
      select: {
        parentId: true,
      },
    });

    if (!folder || !folder.parentId) return false;

    return this.isDescendant(folderId, folder.parentId, userId);
  }

  /**
   * Réorganise l'ordre des dossiers
   */
  async reorderFolders(
    userId: string,
    folderOrders: Array<{ id: string; order: number }>
  ): Promise<void> {
    // Vérifier que tous les dossiers appartiennent à l'utilisateur
    const folderIds = folderOrders.map((f) => f.id);
    const folders = await prisma.folder.findMany({
      where: {
        id: { in: folderIds },
        userId,
      },
    });

    if (folders.length !== folderIds.length) {
      throw new Error('Some folders not found or access denied');
    }

    // Mettre à jour l'ordre
    await Promise.all(
      folderOrders.map((folderOrder) =>
        prisma.folder.update({
          where: { id: folderOrder.id },
          data: { order: folderOrder.order },
        })
      )
    );
  }
}

// Instance singleton
export const foldersService = new FoldersService();
