import { prisma } from '@/services/core/database';
import { Task } from '@/lib/types';
import { Prisma } from '@prisma/client';
import { tagsService } from './task-management/tags';

export interface Note {
  id: string;
  title: string;
  content: string;
  userId: string;
  taskId?: string; // Tâche associée à la note
  folderId?: string; // Dossier contenant la note
  task?: Task | null; // Objet Task complet
  createdAt: Date;
  updatedAt: Date;
  tags?: string[];
}

export interface CreateNoteData {
  title: string;
  content: string;
  userId: string;
  taskId?: string; // Tâche associée à la note
  folderId?: string; // Dossier contenant la note
  tags?: string[];
}

export interface UpdateNoteData {
  title?: string;
  content?: string;
  taskId?: string; // Tâche associée à la note
  folderId?: string; // Dossier contenant la note
  tags?: string[];
}

/**
 * Service pour la gestion des notes markdown
 */
export class NotesService {
  /**
   * Mappe un objet Task Prisma vers l'interface Task
   */
  private mapPrismaTaskToTask(
    prismaTask: Prisma.TaskGetPayload<{
      include: {
        taskTags: {
          include: {
            tag: true;
          };
        };
        primaryTag: true;
      };
    }> | null
  ): Task | null {
    if (!prismaTask) return null;

    // Extraire les tags depuis les relations TaskTag
    let tags: string[] = [];
    let tagDetails: Array<{
      id: string;
      name: string;
      color: string;
      isPinned: boolean;
    }> = [];

    if (prismaTask.taskTags && Array.isArray(prismaTask.taskTags)) {
      tags = prismaTask.taskTags.map((tt) => tt.tag.name);
      tagDetails = prismaTask.taskTags.map((tt) => ({
        id: tt.tag.id,
        name: tt.tag.name,
        color: tt.tag.color,
        isPinned: tt.tag.isPinned,
      }));
    }

    return {
      id: prismaTask.id,
      title: prismaTask.title,
      description: prismaTask.description || undefined,
      status: prismaTask.status as Task['status'],
      priority: prismaTask.priority as Task['priority'],
      source: prismaTask.source as Task['source'],
      sourceId: prismaTask.sourceId || undefined,
      tags,
      tagDetails,
      primaryTagId: prismaTask.primaryTagId || undefined,
      primaryTag: prismaTask.primaryTag || undefined,
      dueDate: prismaTask.dueDate || undefined,
      completedAt: prismaTask.completedAt || undefined,
      createdAt: prismaTask.createdAt,
      updatedAt: prismaTask.updatedAt,
      jiraProject: prismaTask.jiraProject || undefined,
      jiraKey: prismaTask.jiraKey || undefined,
      jiraType: prismaTask.jiraType || undefined,
      tfsProject: prismaTask.tfsProject || undefined,
      tfsPullRequestId: prismaTask.tfsPullRequestId || undefined,
      tfsRepository: prismaTask.tfsRepository || undefined,
      tfsSourceBranch: prismaTask.tfsSourceBranch || undefined,
      tfsTargetBranch: prismaTask.tfsTargetBranch || undefined,
      ownerId: (prismaTask as unknown as { ownerId: string }).ownerId, // Cast temporaire
    };
  }
  /**
   * Récupère toutes les notes d'un utilisateur
   */
  async getNotes(userId: string): Promise<Note[]> {
    const notes = await prisma.note.findMany({
      where: { userId },
      include: {
        noteTags: {
          include: {
            tag: true,
          },
        },
        task: {
          include: {
            taskTags: {
              include: {
                tag: true,
              },
            },
            primaryTag: true,
          },
        },
      },
      orderBy: [{ order: 'asc' }, { updatedAt: 'desc' }],
    });

    return notes.map((note) => ({
      ...note,
      taskId: note.taskId || undefined, // Convertir null en undefined
      folderId: note.folderId || undefined, // Convertir null en undefined
      task: this.mapPrismaTaskToTask(note.task), // Mapper correctement l'objet Task
      tags: note.noteTags.map((nt) => nt.tag.name),
    }));
  }

  /**
   * Récupère une note par son ID
   */
  async getNoteById(noteId: string, userId: string): Promise<Note | null> {
    const note = await prisma.note.findFirst({
      where: {
        id: noteId,
        userId,
      },
      include: {
        noteTags: {
          include: {
            tag: true,
          },
        },
        task: {
          include: {
            taskTags: {
              include: {
                tag: true,
              },
            },
            primaryTag: true,
          },
        },
      },
    });

    if (!note) return null;

    return {
      ...note,
      taskId: note.taskId || undefined, // Convertir null en undefined
      folderId: note.folderId || undefined, // Convertir null en undefined
      task: this.mapPrismaTaskToTask(note.task), // Mapper correctement l'objet Task
      tags: note.noteTags.map((nt) => nt.tag.name),
    };
  }

  /**
   * Crée une nouvelle note
   */
  async createNote(data: CreateNoteData): Promise<Note> {
    const note = await prisma.note.create({
      data: {
        title: data.title,
        content: data.content,
        userId: data.userId,
        taskId: data.taskId, // Ajouter le taskId
        folderId: data.folderId, // Ajouter le folderId
      },
      include: {
        task: {
          include: {
            taskTags: {
              include: {
                tag: true,
              },
            },
            primaryTag: true,
          },
        },
      },
    });

    // Créer les relations avec les tags si fournis
    if (data.tags && data.tags.length > 0) {
      // Créer ou récupérer tous les tags
      const tags = await tagsService.ensureTagsExist(data.tags, data.userId);

      // Créer les relations NoteTag
      await prisma.noteTag.createMany({
        data: tags.map((tag) => ({
          noteId: note.id,
          tagId: tag.id,
        })),
      });
    }

    // Récupérer la note avec les tags
    const noteWithTags = await prisma.note.findUnique({
      where: { id: note.id },
      include: {
        noteTags: {
          include: {
            tag: true,
          },
        },
        task: {
          include: {
            taskTags: {
              include: {
                tag: true,
              },
            },
            primaryTag: true,
          },
        },
      },
    });

    return {
      ...noteWithTags!,
      taskId: noteWithTags!.taskId || undefined, // Convertir null en undefined
      folderId: noteWithTags!.folderId || undefined, // Convertir null en undefined
      task: this.mapPrismaTaskToTask(noteWithTags!.task), // Mapper correctement l'objet Task
      tags: noteWithTags!.noteTags.map((nt) => nt.tag.name),
    };
  }

  /**
   * Met à jour une note existante
   */
  async updateNote(
    noteId: string,
    userId: string,
    data: UpdateNoteData
  ): Promise<Note> {
    // Vérifier que la note appartient à l'utilisateur
    const existingNote = await prisma.note.findFirst({
      where: {
        id: noteId,
        userId,
      },
    });

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

    // Préparer les données de mise à jour
    const updateData: {
      updatedAt: Date;
      title?: string;
      content?: string;
      taskId?: string | null;
      folderId?: string | null;
      noteTags?: {
        deleteMany: Record<string, never>;
        create: Array<{
          tag: {
            connectOrCreate: {
              where: { name: string };
              create: { name: string };
            };
          };
        }>;
      };
    } = {
      updatedAt: new Date(),
    };

    // Ajouter les champs de base s'ils sont fournis
    if (data.title !== undefined) {
      updateData.title = data.title;
    }
    if (data.content !== undefined) {
      updateData.content = data.content;
    }
    if (data.taskId !== undefined) {
      updateData.taskId = data.taskId || null;
    }
    if (data.folderId !== undefined) {
      updateData.folderId = data.folderId || null;
    }

    // Gérer les tags si fournis
    if (data.tags !== undefined) {
      // Supprimer toutes les relations existantes
      await prisma.noteTag.deleteMany({
        where: { noteId: noteId },
      });

      // Créer les nouvelles relations si des tags sont fournis
      if (data.tags.length > 0) {
        // Créer ou récupérer tous les tags
        const tags = await tagsService.ensureTagsExist(data.tags, userId);

        // Créer les relations NoteTag
        await prisma.noteTag.createMany({
          data: tags.map((tag) => ({
            noteId: noteId,
            tagId: tag.id,
          })),
        });
      }
    }

    await prisma.note.update({
      where: { id: noteId },
      data: updateData as Prisma.NoteUpdateInput,
    });

    // Récupérer la note avec les tags après la mise à jour
    const noteWithTags = await prisma.note.findUnique({
      where: { id: noteId },
      include: {
        noteTags: {
          include: {
            tag: true,
          },
        },
        task: {
          include: {
            taskTags: {
              include: {
                tag: true,
              },
            },
            primaryTag: true,
          },
        },
      },
    });

    return {
      ...noteWithTags!,
      taskId: noteWithTags!.taskId || undefined, // Convertir null en undefined
      folderId: noteWithTags!.folderId || undefined, // Convertir null en undefined
      task: this.mapPrismaTaskToTask(noteWithTags!.task), // Mapper correctement l'objet Task
      tags: noteWithTags!.noteTags.map((nt) => nt.tag.name),
    };
  }

  /**
   * Supprime une note
   */
  async deleteNote(noteId: string, userId: string): Promise<void> {
    // Vérifier que la note appartient à l'utilisateur
    const existingNote = await prisma.note.findFirst({
      where: {
        id: noteId,
        userId,
      },
    });

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

    await prisma.note.delete({
      where: { id: noteId },
    });
  }

  /**
   * Recherche des notes par titre ou contenu
   */
  async searchNotes(userId: string, query: string): Promise<Note[]> {
    const notes = await prisma.note.findMany({
      where: {
        userId,
        OR: [{ title: { contains: query } }, { content: { contains: query } }],
      },
      orderBy: { updatedAt: 'desc' },
    });

    return notes.map((note) => ({
      ...note,
      taskId: note.taskId || undefined, // Convertir null en undefined
      folderId: note.folderId || undefined, // Convertir null en undefined
    }));
  }

  /**
   * Réordonne les notes
   */
  async reorderNotes(
    userId: string,
    noteOrders: Array<{ id: string; order: number }>
  ): Promise<void> {
    // Vérifier que toutes les notes appartiennent à l'utilisateur
    const noteIds = noteOrders.map((n) => n.id);
    const notes = await prisma.note.findMany({
      where: {
        id: { in: noteIds },
        userId,
      },
    });

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

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

  /**
   * Récupère les statistiques des notes d'un utilisateur
   */
  async getNotesStats(userId: string): Promise<{
    totalNotes: number;
    totalWords: number;
    lastUpdated: Date | null;
  }> {
    const notes = await prisma.note.findMany({
      where: { userId },
      select: {
        content: true,
        updatedAt: true,
      },
    });

    const totalNotes = notes.length;
    const totalWords = notes.reduce((acc, note) => {
      return (
        acc + note.content.split(/\s+/).filter((word) => word.length > 0).length
      );
    }, 0);
    const lastUpdated =
      notes.length > 0
        ? notes.reduce(
            (latest, note) =>
              note.updatedAt > latest ? note.updatedAt : latest,
            notes[0].updatedAt
          )
        : null;

    return {
      totalNotes,
      totalWords,
      lastUpdated,
    };
  }
}

// Instance singleton
export const notesService = new NotesService();
