import { prisma } from '@/services/core/database';
import {
  eachDayOfInterval,
  format,
  startOfDay,
  endOfDay,
  startOfWeek,
  endOfWeek,
} from 'date-fns';
import { fr } from 'date-fns/locale';
import {
  formatDateForAPI,
  getDayName,
  getToday,
  subtractDays,
} from '@/lib/date-utils';

export interface DailyMetrics {
  date: string; // Format ISO
  dayName: string; // Lundi, Mardi, etc.
  completed: number;
  inProgress: number;
  blocked: number;
  pending: number;
  newTasks: number;
  totalTasks: number;
  completionRate: number;
}

export interface VelocityTrend {
  date: string;
  completed: number;
  created: number;
  velocity: number;
}

export interface WeeklyMetricsOverview {
  period: {
    start: Date;
    end: Date;
  };
  dailyBreakdown: DailyMetrics[];
  summary: {
    totalTasksCompleted: number;
    totalTasksCreated: number;
    averageCompletionRate: number;
    peakProductivityDay: string;
    lowProductivityDay: string;
    trendsAnalysis: {
      completionTrend: 'improving' | 'declining' | 'stable';
      productivityPattern: 'consistent' | 'variable' | 'weekend-heavy';
    };
  };
  statusDistribution: {
    status: string;
    count: number;
    percentage: number;
    color: string;
  }[];
  priorityBreakdown: {
    priority: string;
    completed: number;
    pending: number;
    total: number;
    completionRate: number;
    color: string;
  }[];
}

export class MetricsService {
  /**
   * Récupère les métriques journalières des 7 derniers jours
   */
  static async getWeeklyMetrics(
    userId: string,
    date: Date = getToday()
  ): Promise<WeeklyMetricsOverview> {
    // Fenêtre glissante de 7 jours au lieu de semaine calendaire
    const weekEnd = new Date(date);
    const weekStart = new Date(date);
    weekStart.setDate(weekStart.getDate() - 6); // 7 jours en arrière (incluant aujourd'hui)

    // Générer tous les jours de la semaine
    const daysOfWeek = eachDayOfInterval({ start: weekStart, end: weekEnd });

    // Récupérer les données pour chaque jour
    const dailyBreakdown = await Promise.all(
      daysOfWeek.map((day) => this.getDailyMetrics(userId, day))
    );

    // Calculer les métriques de résumé
    const summary = this.calculateWeeklySummary(dailyBreakdown);

    // Récupérer la distribution des statuts pour la semaine
    const statusDistribution = await this.getStatusDistribution(
      weekStart,
      weekEnd
    );

    // Récupérer la répartition par priorité
    const priorityBreakdown = await this.getPriorityBreakdown(
      weekStart,
      weekEnd
    );

    return {
      period: { start: weekStart, end: weekEnd },
      dailyBreakdown,
      summary,
      statusDistribution,
      priorityBreakdown,
    };
  }

  /**
   * Récupère les métriques pour un jour donné
   */
  private static async getDailyMetrics(
    userId: string,
    date: Date
  ): Promise<DailyMetrics> {
    const dayStart = startOfDay(date);
    const dayEnd = endOfDay(date);

    // Compter les tâches par statut à la fin de la journée
    const [completed, inProgress, blocked, pending, newTasks, totalTasks] =
      await Promise.all([
        // Tâches complétées ce jour
        prisma.task.count({
          where: {
            ownerId: userId,
            OR: [
              {
                completedAt: {
                  gte: dayStart,
                  lte: dayEnd,
                },
              },
              {
                status: 'done',
                updatedAt: {
                  gte: dayStart,
                  lte: dayEnd,
                },
              },
            ],
          },
        }),

        // Tâches en cours (status = in_progress à ce moment)
        prisma.task.count({
          where: {
            ownerId: userId,
            status: 'in_progress',
            createdAt: { lte: dayEnd },
          },
        }),

        // Tâches bloquées
        prisma.task.count({
          where: {
            ownerId: userId,
            status: 'blocked',
            createdAt: { lte: dayEnd },
          },
        }),

        // Tâches en attente
        prisma.task.count({
          where: {
            ownerId: userId,
            status: 'pending',
            createdAt: { lte: dayEnd },
          },
        }),

        // Nouvelles tâches créées ce jour
        prisma.task.count({
          where: {
            ownerId: userId,
            createdAt: {
              gte: dayStart,
              lte: dayEnd,
            },
          },
        }),

        // Total des tâches existantes ce jour
        prisma.task.count({
          where: {
            ownerId: userId,
            createdAt: { lte: dayEnd },
          },
        }),
      ]);

    const completionRate = totalTasks > 0 ? (completed / totalTasks) * 100 : 0;

    return {
      date: formatDateForAPI(date),
      dayName: getDayName(date),
      completed,
      inProgress,
      blocked,
      pending,
      newTasks,
      totalTasks,
      completionRate: Math.round(completionRate * 100) / 100,
    };
  }

  /**
   * Calcule le résumé hebdomadaire
   */
  private static calculateWeeklySummary(dailyBreakdown: DailyMetrics[]) {
    const totalTasksCompleted = dailyBreakdown.reduce(
      (sum, day) => sum + day.completed,
      0
    );
    const totalTasksCreated = dailyBreakdown.reduce(
      (sum, day) => sum + day.newTasks,
      0
    );
    const averageCompletionRate =
      dailyBreakdown.reduce((sum, day) => sum + day.completionRate, 0) /
      dailyBreakdown.length;

    // Identifier les jours de pic et de creux
    const peakDay = dailyBreakdown.reduce((peak, day) =>
      day.completed > peak.completed ? day : peak
    );
    const lowDay = dailyBreakdown.reduce((low, day) =>
      day.completed < low.completed ? day : low
    );

    // Analyser les tendances
    const firstHalf = dailyBreakdown.slice(0, 3);
    const secondHalf = dailyBreakdown.slice(4);
    const firstHalfAvg =
      firstHalf.reduce((sum, day) => sum + day.completed, 0) / firstHalf.length;
    const secondHalfAvg =
      secondHalf.reduce((sum, day) => sum + day.completed, 0) /
      secondHalf.length;

    let completionTrend: 'improving' | 'declining' | 'stable';
    if (secondHalfAvg > firstHalfAvg * 1.1) {
      completionTrend = 'improving';
    } else if (secondHalfAvg < firstHalfAvg * 0.9) {
      completionTrend = 'declining';
    } else {
      completionTrend = 'stable';
    }

    // Analyser le pattern de productivité
    const weekendDays = dailyBreakdown.slice(5); // Samedi et dimanche
    const weekdayDays = dailyBreakdown.slice(0, 5);
    const weekendAvg =
      weekendDays.reduce((sum, day) => sum + day.completed, 0) /
      weekendDays.length;
    const weekdayAvg =
      weekdayDays.reduce((sum, day) => sum + day.completed, 0) /
      weekdayDays.length;

    let productivityPattern: 'consistent' | 'variable' | 'weekend-heavy';
    if (weekendAvg > weekdayAvg * 1.2) {
      productivityPattern = 'weekend-heavy';
    } else {
      const variance =
        dailyBreakdown.reduce((sum, day) => {
          const diff =
            day.completed - totalTasksCompleted / dailyBreakdown.length;
          return sum + diff * diff;
        }, 0) / dailyBreakdown.length;
      productivityPattern = variance > 4 ? 'variable' : 'consistent';
    }

    return {
      totalTasksCompleted,
      totalTasksCreated,
      averageCompletionRate: Math.round(averageCompletionRate * 100) / 100,
      peakProductivityDay: peakDay.dayName,
      lowProductivityDay: lowDay.dayName,
      trendsAnalysis: {
        completionTrend,
        productivityPattern,
      },
    };
  }

  /**
   * Récupère la distribution des statuts pour la période
   */
  private static async getStatusDistribution(start: Date, end: Date) {
    const statusCounts = await prisma.task.groupBy({
      by: ['status'],
      _count: {
        status: true,
      },
      where: {
        createdAt: {
          gte: start,
          lte: end,
        },
      },
    });

    const total = statusCounts.reduce(
      (sum, item) => sum + item._count.status,
      0
    );

    const statusColors: { [key: string]: string } = {
      pending: '#94a3b8', // gray
      in_progress: '#3b82f6', // blue
      blocked: '#ef4444', // red
      done: '#10b981', // green
      archived: '#6b7280', // gray-500
    };

    return statusCounts.map((item) => ({
      status: item.status,
      count: item._count.status,
      percentage: Math.round((item._count.status / total) * 100 * 100) / 100,
      color: statusColors[item.status] || '#6b7280',
    }));
  }

  /**
   * Récupère la répartition par priorité avec taux de completion
   */
  private static async getPriorityBreakdown(start: Date, end: Date) {
    const priorities = ['high', 'medium', 'low'];

    const priorityData = await Promise.all(
      priorities.map(async (priority) => {
        const [completed] = await Promise.all([
          // Tâches complétées dans la période
          prisma.task.count({
            where: {
              priority,
              completedAt: {
                gte: start,
                lte: end,
              },
            },
          }),
        ]);

        // Calculer les tâches en cours de cette priorité
        const inProgress = await prisma.task.count({
          where: {
            priority,
            status: {
              notIn: ['done', 'cancelled', 'archived'],
            },
          },
        });

        // Le total pour le calcul de completion = complétées + en cours
        const total = completed + inProgress;

        const pending = total - completed;
        const completionRate = total > 0 ? (completed / total) * 100 : 0;

        return {
          priority,
          completed,
          pending,
          total,
          completionRate: Math.round(completionRate * 100) / 100,
          color:
            priority === 'high'
              ? '#ef4444'
              : priority === 'medium'
                ? '#f59e0b'
                : '#10b981',
        };
      })
    );

    return priorityData;
  }

  /**
   * Récupère les métriques de vélocité d'équipe (pour graphiques de tendance)
   */
  static async getVelocityTrends(
    userId: string,
    weeksBack: number = 4
  ): Promise<VelocityTrend[]> {
    const trends = [];

    for (let i = weeksBack - 1; i >= 0; i--) {
      const weekStart = startOfWeek(subtractDays(getToday(), i * 7), {
        weekStartsOn: 1,
      });
      const weekEnd = endOfWeek(weekStart, { weekStartsOn: 1 });

      const [completed, created] = await Promise.all([
        prisma.task.count({
          where: {
            ownerId: userId,
            completedAt: {
              gte: weekStart,
              lte: weekEnd,
            },
          },
        }),
        prisma.task.count({
          where: {
            ownerId: userId,
            createdAt: {
              gte: weekStart,
              lte: weekEnd,
            },
          },
        }),
      ]);

      const velocity = created > 0 ? (completed / created) * 100 : 0;

      trends.push({
        date: format(weekStart, 'dd/MM', { locale: fr }),
        completed,
        created,
        velocity: Math.round(velocity * 100) / 100,
      });
    }

    return trends;
  }
}
