import { DiaryDate, Member, Participant } from '../models/index.js';
import { Op } from 'sequelize';

function getIsoWeekKey(dateLike) {
  const date = new Date(dateLike);
  if (Number.isNaN(date.getTime())) {
    return null;
  }

  const utcDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
  const dayNum = utcDate.getUTCDay() || 7;
  utcDate.setUTCDate(utcDate.getUTCDate() + 4 - dayNum);
  const yearStart = new Date(Date.UTC(utcDate.getUTCFullYear(), 0, 1));
  const weekNum = Math.ceil((((utcDate - yearStart) / 86400000) + 1) / 7);
  return `${utcDate.getUTCFullYear()}-${String(weekNum).padStart(2, '0')}`;
}

function countMissedTrainingWeeks(trainingDates, lastTrainingDate) {
  const lastTrainingTs = lastTrainingDate ? new Date(lastTrainingDate).getTime() : null;
  const weeks = new Set();

  for (const trainingDate of trainingDates) {
    const ts = new Date(trainingDate).getTime();
    if (Number.isNaN(ts)) {
      continue;
    }
    if (lastTrainingTs !== null && ts <= lastTrainingTs) {
      continue;
    }
    const key = getIsoWeekKey(trainingDate);
    if (key) {
      weeks.add(key);
    }
  }

  return weeks.size;
}

class TrainingStatsService {
  async getTrainingStats(clubIdRaw) {
    const clubId = parseInt(clubIdRaw, 10);
    if (!Number.isFinite(clubId)) {
      const err = new Error('Ungültige clubId');
      err.status = 400;
      throw err;
    }

    const now = new Date();
    const twelveMonthsAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
    const threeMonthsAgo = new Date(now.getFullYear(), now.getMonth() - 3, now.getDate());

    const members = await Member.findAll({
      where: { active: true, clubId }
    });

    const trainingsCount12Months = await DiaryDate.count({
      where: {
        clubId,
        date: { [Op.gte]: twelveMonthsAgo }
      }
    });

    const trainingsCount3Months = await DiaryDate.count({
      where: {
        clubId,
        date: { [Op.gte]: threeMonthsAgo }
      }
    });

    const stats = [];
    const trainingDates12Months = await DiaryDate.findAll({
      where: {
        clubId,
        date: { [Op.gte]: twelveMonthsAgo }
      },
      attributes: ['date'],
      order: [['date', 'DESC']]
    });
    const trainingDateValues12Months = trainingDates12Months.map(entry => entry.date);

    for (const member of members) {
      const participation12Months = await Participant.count({
        include: [{
          model: DiaryDate,
          as: 'diaryDate',
          where: {
            clubId,
            date: { [Op.gte]: twelveMonthsAgo }
          }
        }],
        where: { memberId: member.id }
      });

      const participation3Months = await Participant.count({
        include: [{
          model: DiaryDate,
          as: 'diaryDate',
          where: {
            clubId,
            date: { [Op.gte]: threeMonthsAgo }
          }
        }],
        where: { memberId: member.id }
      });

      const participationTotal = await Participant.count({
        include: [{
          model: DiaryDate,
          as: 'diaryDate',
          where: { clubId }
        }],
        where: { memberId: member.id }
      });

      const trainingDetails = await Participant.findAll({
        include: [{
          model: DiaryDate,
          as: 'diaryDate',
          where: { clubId }
        }],
        where: { memberId: member.id },
        order: [['diaryDate', 'date', 'DESC']],
        limit: 50
      });

      const formattedTrainingDetails = trainingDetails.map((participation) => ({
        id: participation.id,
        date: participation.diaryDate.date,
        activityName: 'Training',
        startTime: '--:--',
        endTime: '--:--'
      }));

      const lastTrainingDate = trainingDetails.length ? trainingDetails[0].diaryDate.date : null;
      const lastTrainingTs = lastTrainingDate ? new Date(lastTrainingDate).getTime() : 0;
      const missedTrainingWeeks = countMissedTrainingWeeks(trainingDateValues12Months, lastTrainingDate);
      const notInTraining = missedTrainingWeeks >= 6;

      stats.push({
        id: member.id,
        firstName: member.firstName,
        lastName: member.lastName,
        birthDate: member.birthDate,
        participation12Months,
        participation3Months,
        participationTotal,
        lastTraining: lastTrainingDate,
        lastTrainingTs,
        missedTrainingWeeks,
        notInTraining,
        trainingDetails: formattedTrainingDetails
      });
    }

    stats.sort((a, b) => b.participationTotal - a.participationTotal);

    const trainingDays = await DiaryDate.findAll({
      where: {
        clubId,
        date: { [Op.gte]: twelveMonthsAgo }
      },
      include: [{
        model: Participant,
        as: 'participantList',
        attributes: ['id']
      }],
      order: [['date', 'DESC']]
    });

    const formattedTrainingDays = trainingDays.map((day) => ({
      id: day.id,
      date: day.date,
      participantCount: day.participantList ? day.participantList.length : 0
    }));

    return {
      members: stats,
      trainingsCount12Months,
      trainingsCount3Months,
      trainingDays: formattedTrainingDays
    };
  }
}

export default new TrainingStatsService();
