import { prisma } from '@/services/core/database';
import { Task, TaskStatus, TaskPriority, TaskSource } from '@/lib/types';
import { getToday, subtractDays } from '@/lib/date-utils';
import { getTagColor } from '@/lib/tag-colors';

export interface TagDistributionMetrics {
  tagDistribution: Array<{
    tagName: string;
    tagColor: string;
    count: number;
    percentage: number;
    tasks: Array<{
      id: string;
      title: string;
      status: TaskStatus;
      priority: TaskPriority;
    }>;
  }>;
  topTags: Array<{
    tagName: string;
    tagColor: string;
    usage: number;
    completionRate: number;
    avgPriority: number;
  }>;
  tagTrends: Array<{
    date: string;
    tagName: string;
    count: number;
  }>;
  tagStats: {
    totalTags: number;
    activeTags: number;
    mostUsedTag: string;
    leastUsedTag: string;
    avgTasksPerTag: number;
  };
}

export interface TimeRange {
  start: Date;
  end: Date;
}

export class TagAnalyticsService {
  /**
   * Calcule les métriques de distribution par tags
   */
  static async getTagDistributionMetrics(
    userId: string,
    timeRange?: TimeRange,
    sources?: string[]
  ): Promise<TagDistributionMetrics> {
    try {
      const now = getToday();
      const defaultStart = subtractDays(now, 30); // 30 jours

      const start = timeRange?.start || defaultStart;
      const end = timeRange?.end || now;

      // Récupérer toutes les tâches avec leurs tags
      const dbTasks = await prisma.task.findMany({
        where: {
          ownerId: userId,
          createdAt: {
            gte: start,
            lte: end,
          },
        },
        include: {
          taskTags: {
            include: {
              tag: true,
            },
          },
        },
      });

      // Récupérer aussi tous les tags pour avoir leurs couleurs
      const allTags = await prisma.tag.findMany({
        select: {
          name: true,
          color: true,
        },
      });

      const tagColorMap = new Map(allTags.map((tag) => [tag.name, tag.color]));

      // Convertir en format Task
      let tasks: Task[] = dbTasks.map((task) => ({
        id: task.id,
        title: task.title,
        description: task.description || undefined,
        status: task.status as TaskStatus,
        priority: task.priority as TaskPriority,
        source: task.source as TaskSource,
        sourceId: task.sourceId || undefined,
        tags: task.taskTags.map((taskTag) => taskTag.tag.name),
        dueDate: task.dueDate || undefined,
        completedAt: task.completedAt || undefined,
        createdAt: task.createdAt,
        updatedAt: task.updatedAt,
        jiraProject: task.jiraProject || undefined,
        jiraKey: task.jiraKey || undefined,
        jiraType: task.jiraType || undefined,
        assignee: task.assignee || undefined,
        ownerId: task.ownerId,
      }));

      // Filtrer par sources si spécifié
      if (sources && sources.length > 0) {
        tasks = tasks.filter((task) => sources.includes(task.source));
      }

      return {
        tagDistribution: this.calculateTagDistribution(tasks, tagColorMap),
        topTags: this.calculateTopTags(tasks, tagColorMap),
        tagTrends: this.calculateTagTrends(tasks),
        tagStats: this.calculateTagStats(tasks),
      };
    } catch (error) {
      console.error('Erreur lors du calcul des métriques de tags:', error);
      throw new Error(
        'Impossible de calculer les métriques de distribution par tags'
      );
    }
  }

  /**
   * Calcule la distribution des tags
   */
  private static calculateTagDistribution(
    tasks: Task[],
    tagColorMap: Map<string, string>
  ) {
    const tagCounts = new Map<string, { color: string; tasks: Task[] }>();

    // Compter les tâches par tag
    tasks.forEach((task) => {
      task.tags.forEach((tagName) => {
        if (!tagCounts.has(tagName)) {
          const tagColor = getTagColor(tagName, tagColorMap.get(tagName));
          tagCounts.set(tagName, { color: tagColor, tasks: [] });
        }
        tagCounts.get(tagName)!.tasks.push(task);
      });
    });

    const totalTasks = tasks.length;

    return Array.from(tagCounts.entries())
      .map(([tagName, data]) => ({
        tagName,
        tagColor: data.color,
        count: data.tasks.length,
        percentage: totalTasks > 0 ? (data.tasks.length / totalTasks) * 100 : 0,
        tasks: data.tasks.map((task) => ({
          id: task.id,
          title: task.title,
          status: task.status,
          priority: task.priority,
        })),
      }))
      .sort((a, b) => b.count - a.count);
  }

  /**
   * Calcule les tags les plus utilisés avec métriques
   */
  private static calculateTopTags(
    tasks: Task[],
    tagColorMap: Map<string, string>
  ) {
    const tagMetrics = new Map<
      string,
      {
        color: string;
        totalTasks: number;
        completedTasks: number;
        prioritySum: number;
        priorityCount: number;
      }
    >();

    // Calculer les métriques par tag
    tasks.forEach((task) => {
      task.tags.forEach((tagName) => {
        if (!tagMetrics.has(tagName)) {
          const tagColor = getTagColor(tagName, tagColorMap.get(tagName));
          tagMetrics.set(tagName, {
            color: tagColor,
            totalTasks: 0,
            completedTasks: 0,
            prioritySum: 0,
            priorityCount: 0,
          });
        }

        const metrics = tagMetrics.get(tagName)!;
        metrics.totalTasks++;

        if (task.status === 'done') {
          metrics.completedTasks++;
        }

        // Convertir la priorité en nombre pour le calcul de moyenne
        const priorityValue =
          task.priority === 'high' ? 3 : task.priority === 'medium' ? 2 : 1;
        metrics.prioritySum += priorityValue;
        metrics.priorityCount++;
      });
    });

    return Array.from(tagMetrics.entries())
      .map(([tagName, metrics]) => ({
        tagName,
        tagColor: metrics.color,
        usage: metrics.totalTasks,
        completionRate:
          metrics.totalTasks > 0
            ? (metrics.completedTasks / metrics.totalTasks) * 100
            : 0,
        avgPriority:
          metrics.priorityCount > 0
            ? metrics.prioritySum / metrics.priorityCount
            : 0,
      }))
      .sort((a, b) => b.usage - a.usage)
      .slice(0, 10); // Top 10
  }

  /**
   * Calcule les tendances des tags dans le temps
   */
  private static calculateTagTrends(tasks: Task[]) {
    const trends = new Map<string, Map<string, number>>();

    // Grouper par jour et par tag
    tasks.forEach((task) => {
      const day = task.createdAt.toISOString().split('T')[0];
      task.tags.forEach((tagName) => {
        if (!trends.has(tagName)) {
          trends.set(tagName, new Map());
        }
        const dayMap = trends.get(tagName)!;
        dayMap.set(day, (dayMap.get(day) || 0) + 1);
      });
    });

    // Convertir en format pour le graphique
    const result: Array<{ date: string; tagName: string; count: number }> = [];
    trends.forEach((dayMap, tagName) => {
      dayMap.forEach((count, date) => {
        result.push({ date, tagName, count });
      });
    });

    return result.sort((a, b) => a.date.localeCompare(b.date));
  }

  /**
   * Calcule les statistiques générales des tags
   */
  private static calculateTagStats(tasks: Task[]) {
    const allTags = new Set<string>();
    const tagUsage = new Map<string, number>();

    tasks.forEach((task) => {
      task.tags.forEach((tagName) => {
        allTags.add(tagName);
        tagUsage.set(tagName, (tagUsage.get(tagName) || 0) + 1);
      });
    });

    const usageValues = Array.from(tagUsage.values());
    const mostUsedTag =
      Array.from(tagUsage.entries()).sort((a, b) => b[1] - a[1])[0]?.[0] ||
      'Aucun';
    const leastUsedTag =
      Array.from(tagUsage.entries()).sort((a, b) => a[1] - b[1])[0]?.[0] ||
      'Aucun';

    return {
      totalTags: allTags.size,
      activeTags: Array.from(tagUsage.values()).filter((count) => count > 0)
        .length,
      mostUsedTag,
      leastUsedTag,
      avgTasksPerTag:
        allTags.size > 0
          ? usageValues.reduce((sum, count) => sum + count, 0) / allTags.size
          : 0,
    };
  }
}
