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

export interface ProductivityMetrics {
  completionTrend: Array<{
    date: string;
    completed: number;
    created: number;
    total: number;
  }>;
  velocityData: Array<{
    week: string;
    completed: number;
    average: number;
    weekNumber?: number;
    weekStart?: Date;
  }>;
  priorityDistribution: Array<{
    priority: string;
    count: number;
    percentage: number;
  }>;
  statusFlow: Array<{
    status: string;
    count: number;
    percentage: number;
  }>;
  weeklyStats: {
    thisWeek: number;
    lastWeek: number;
    change: number;
    changePercent: number;
  };
}

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

export class AnalyticsService {
  /**
   * Calcule les métriques de productivité pour une période donnée
   */
  static async getProductivityMetrics(
    userId: string,
    timeRange?: TimeRange,
    sources?: string[]
  ): Promise<ProductivityMetrics> {
    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 depuis la base de données avec leurs tags
      const dbTasks = await prisma.task.findMany({
        where: {
          ownerId: userId,
        },
        include: {
          taskTags: {
            include: {
              tag: true,
            },
          },
        },
      });

      // 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 {
        completionTrend: this.calculateCompletionTrend(tasks, start, end),
        velocityData: this.calculateVelocity(tasks, start, end),
        priorityDistribution: this.calculatePriorityDistribution(tasks),
        statusFlow: this.calculateStatusFlow(tasks),
        weeklyStats: this.calculateWeeklyStats(tasks),
      };
    } catch (error) {
      console.error('Erreur lors du calcul des métriques:', error);
      throw new Error('Impossible de calculer les métriques de productivité');
    }
  }

  /**
   * Calcule la tendance de completion des tâches par jour
   */
  private static calculateCompletionTrend(
    tasks: Task[],
    start: Date,
    end: Date
  ) {
    const trend: Array<{
      date: string;
      completed: number;
      created: number;
      total: number;
    }> = [];

    // Générer les dates pour la période
    const currentDate = new Date(start.getTime());
    while (currentDate <= end) {
      const dateStr = currentDate.toISOString().split('T')[0];

      // Compter les tâches terminées ce jour
      const completedThisDay = tasks.filter(
        (task) =>
          task.completedAt &&
          task.completedAt.toISOString().split('T')[0] === dateStr
      ).length;

      // Compter les tâches créées ce jour
      const createdThisDay = tasks.filter(
        (task) => task.createdAt.toISOString().split('T')[0] === dateStr
      ).length;

      // Total cumulé jusqu'à ce jour
      const totalUntilThisDay = tasks.filter(
        (task) => task.createdAt <= currentDate
      ).length;

      trend.push({
        date: dateStr,
        completed: completedThisDay,
        created: createdThisDay,
        total: totalUntilThisDay,
      });

      currentDate.setDate(currentDate.getDate() + 1);
    }

    return trend;
  }

  /**
   * Calcule la vélocité (tâches terminées par semaine)
   */
  private static calculateVelocity(tasks: Task[], start: Date, end: Date) {
    const weeklyData: Array<{
      week: string;
      completed: number;
      average: number;
      weekNumber: number;
      weekStart: Date;
    }> = [];
    const completedTasks = tasks.filter((task) => task.completedAt);

    // Grouper par semaine en utilisant le numéro de semaine comme clé
    const weekGroups = new Map<number, { count: number; weekStart: Date }>();

    completedTasks.forEach((task) => {
      if (
        task.completedAt &&
        task.completedAt >= start &&
        task.completedAt <= end
      ) {
        const weekStart = this.getWeekStart(task.completedAt);
        const weekNumber = this.getWeekNumber(weekStart);

        if (!weekGroups.has(weekNumber)) {
          weekGroups.set(weekNumber, { count: 0, weekStart });
        }
        weekGroups.get(weekNumber)!.count++;
      }
    });

    // Calculer la moyenne mobile
    const values = Array.from(weekGroups.values()).map((w) => w.count);
    const average =
      values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : 0;

    // Convertir en format pour le graphique
    weekGroups.forEach((data, weekNumber) => {
      weeklyData.push({
        week: `Sem. ${weekNumber}`,
        completed: data.count,
        average: Math.round(average * 10) / 10,
        weekNumber,
        weekStart: data.weekStart,
      });
    });

    // Trier par numéro de semaine (pas alphabétiquement)
    return weeklyData.sort((a, b) => a.weekNumber - b.weekNumber);
  }

  /**
   * Calcule la distribution des priorités
   */
  private static calculatePriorityDistribution(tasks: Task[]) {
    const priorityCounts = new Map<string, number>();
    const total = tasks.length;

    tasks.forEach((task) => {
      const priority = task.priority || 'non-définie';
      priorityCounts.set(priority, (priorityCounts.get(priority) || 0) + 1);
    });

    return Array.from(priorityCounts.entries()).map(([priority, count]) => ({
      priority: this.getPriorityLabel(priority),
      count,
      percentage: Math.round((count / total) * 100),
    }));
  }

  /**
   * Calcule la distribution des statuts
   */
  private static calculateStatusFlow(tasks: Task[]) {
    const statusCounts = new Map<string, number>();
    const total = tasks.length;

    tasks.forEach((task) => {
      const status = task.status;
      statusCounts.set(status, (statusCounts.get(status) || 0) + 1);
    });

    return Array.from(statusCounts.entries()).map(([status, count]) => ({
      status: this.getStatusLabel(status),
      count,
      percentage: Math.round((count / total) * 100),
    }));
  }

  /**
   * Calcule les statistiques hebdomadaires
   */
  private static calculateWeeklyStats(tasks: Task[]) {
    const now = getToday();
    const thisWeekStart = this.getWeekStart(now);
    const lastWeekStart = subtractDays(thisWeekStart, 7);
    const lastWeekEnd = subtractDays(thisWeekStart, 1);

    const thisWeekCompleted = tasks.filter(
      (task) =>
        task.completedAt &&
        task.completedAt >= thisWeekStart &&
        task.completedAt <= now
    ).length;

    const lastWeekCompleted = tasks.filter(
      (task) =>
        task.completedAt &&
        task.completedAt >= lastWeekStart &&
        task.completedAt <= lastWeekEnd
    ).length;

    const change = thisWeekCompleted - lastWeekCompleted;
    const changePercent =
      lastWeekCompleted > 0
        ? Math.round((change / lastWeekCompleted) * 100)
        : thisWeekCompleted > 0
          ? 100
          : 0;

    return {
      thisWeek: thisWeekCompleted,
      lastWeek: lastWeekCompleted,
      change,
      changePercent,
    };
  }

  /**
   * Obtient le début de la semaine pour une date
   */
  private static getWeekStart(date: Date): Date {
    const d = new Date(date.getTime());
    const day = d.getDay();
    const diff = d.getDate() - day + (day === 0 ? -6 : 1); // Lundi = début de semaine
    return new Date(d.setDate(diff));
  }

  /**
   * Obtient le numéro de la semaine
   */
  private static getWeekNumber(date: Date): number {
    const d = new Date(
      Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())
    );
    const dayNum = d.getUTCDay() || 7;
    d.setUTCDate(d.getUTCDate() + 4 - dayNum);
    const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
    return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
  }

  /**
   * Convertit le code priorité en label français
   */
  private static getPriorityLabel(priority: string): string {
    const labels: Record<string, string> = {
      low: 'Faible',
      medium: 'Moyenne',
      high: 'Élevée',
      urgent: 'Urgente',
      'non-définie': 'Non définie',
    };
    return labels[priority] || priority;
  }

  /**
   * Convertit le code statut en label français
   */
  private static getStatusLabel(status: string): string {
    const labels: Record<string, string> = {
      backlog: 'Backlog',
      todo: 'À faire',
      in_progress: 'En cours',
      done: 'Terminé',
      cancelled: 'Annulé',
      freeze: 'Gelé',
      archived: 'Archivé',
    };
    return labels[status] || status;
  }
}
