const database = require('../config/database');
const { Op } = require('sequelize');

/**
 * Service-Klasse für Arbeitstage-Statistiken
 * Berechnet verschiedene Tage-Typen für ein Jahr
 */
class WorkdaysService {
  constructor() {
    this.defaultUserId = 1;
  }

  /**
   * Berechnet Arbeitstage-Statistiken für ein Jahr
   * @param {number} userId - Benutzer-ID
   * @param {number} year - Jahr
   * @returns {Promise<Object>} Statistiken
   */
  async getWorkdaysStatistics(userId, year) {
    const { Holiday, Sick, Vacation, User, State } = database.getModels();
    const sequelize = database.sequelize;
    
    // Hole User-Bundesland
    const user = await User.findByPk(userId, { 
      attributes: ['state_id'],
      raw: true 
    });
    const userStateId = user?.state_id;
    
    // Berechne Anzahl Werktage (Mo-Fr) im Jahr
    const workdays = this._countWorkdays(year);
    
    // Hole alle Feiertage für das Jahr mit States
    const holidays = await Holiday.findAll({
      where: {
        date: {
          [Op.gte]: `${year}-01-01`,
          [Op.lte]: `${year}-12-31`
        }
      },
      include: [
        {
          model: State,
          as: 'states',
          attributes: ['id'],
          through: { attributes: [] },
          required: false
        }
      ]
    });
    
    // Zähle nur Feiertage die auf Werktage fallen UND für den User gelten
    const holidayCount = holidays.filter(h => {
      const holidayDate = new Date(h.date + 'T00:00:00');
      const dayOfWeek = holidayDate.getDay();
      
      if (dayOfWeek < 1 || dayOfWeek > 5) {
        return false; // Kein Werktag
      }
      
      // Feiertag gilt wenn: Keine States (Bundesfeiertag) ODER User's State ist dabei
      const holidayStates = h.states || [];
      const isFederal = holidayStates.length === 0;
      const appliesToUser = isFederal || holidayStates.some(s => s.id === userStateId);
      
      return appliesToUser;
    }).length;
    
    // Hole alle Krankheitstage für das Jahr
    const sickEntries = await Sick.findAll({
      where: {
        user_id: userId,
        [Op.or]: [
          {
            first_day: {
              [Op.between]: [`${year}-01-01`, `${year}-12-31`]
            }
          },
          {
            last_day: {
              [Op.between]: [`${year}-01-01`, `${year}-12-31`]
            }
          },
          {
            [Op.and]: [
              { first_day: { [Op.lte]: `${year}-01-01` } },
              { last_day: { [Op.gte]: `${year}-12-31` } }
            ]
          }
        ]
      },
      raw: true
    });
    
    // Zähle Krankheitstage (nur Werktage)
    let sickDays = 0;
    sickEntries.forEach(sick => {
      const start = new Date(Math.max(new Date(sick.first_day + 'T00:00:00'), new Date(`${year}-01-01T00:00:00`)));
      const end = new Date(Math.min(new Date(sick.last_day + 'T00:00:00'), new Date(`${year}-12-31T00:00:00`)));
      
      for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
        const dayOfWeek = d.getDay();
        if (dayOfWeek >= 1 && dayOfWeek <= 5) { // Mo-Fr
          sickDays++;
        }
      }
    });
    
    // Hole alle Urlaubstage für das Jahr
    const vacationEntries = await Vacation.findAll({
      where: {
        user_id: userId,
        [Op.or]: [
          {
            first_day: {
              [Op.between]: [`${year}-01-01`, `${year}-12-31`]
            }
          },
          {
            last_day: {
              [Op.between]: [`${year}-01-01`, `${year}-12-31`]
            }
          },
          {
            [Op.and]: [
              { first_day: { [Op.lte]: `${year}-01-01` } },
              { last_day: { [Op.gte]: `${year}-12-31` } }
            ]
          }
        ]
      },
      raw: true
    });
    
    // Zähle Urlaubstage (nur Werktage, berücksichtige halbe Tage)
    let vacationDays = 0;
    vacationEntries.forEach(vac => {
      const start = new Date(Math.max(new Date(vac.first_day + 'T00:00:00'), new Date(`${year}-01-01T00:00:00`)));
      const end = new Date(Math.min(new Date(vac.last_day + 'T00:00:00'), new Date(`${year}-12-31T00:00:00`)));
      
      const isHalfDay = vac.half_day === 1;
      
      for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
        const dayOfWeek = d.getDay();
        if (dayOfWeek >= 1 && dayOfWeek <= 5) { // Mo-Fr
          vacationDays += isHalfDay ? 0.5 : 1;
        }
      }
    });
    
    // Zähle gearbeitete Tage (Tage mit worklog-Einträgen)
    const workedDaysQuery = `
      SELECT COUNT(DISTINCT DATE(tstamp)) as count
      FROM worklog
      WHERE user_id = :userId
        AND DATE(tstamp) BETWEEN :startDate AND :endDate
        AND state LIKE '%start work%'
    `;
    
    const workedDaysResult = await sequelize.query(workedDaysQuery, {
      replacements: {
        userId,
        startDate: `${year}-01-01`,
        endDate: `${year}-12-31`
      },
      type: sequelize.QueryTypes.SELECT
    });
    
    const workedDays = workedDaysResult[0]?.count || 0;
    
    // Berechne Prozentsatz Krankheitstage
    const sickPercentage = workdays > 0 ? Math.round((sickDays / workdays) * 100) : 0;
    
    return {
      year,
      workdays,
      holidays: holidayCount,
      sickDays,
      sickPercentage,
      vacationDays,
      workedDays
    };
  }

  /**
   * Zählt Werktage (Mo-Fr) in einem Jahr
   * @param {number} year - Jahr
   * @returns {number} Anzahl Werktage
   */
  _countWorkdays(year) {
    let count = 0;
    const start = new Date(year, 0, 1);
    const end = new Date(year, 11, 31);
    
    for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
      const dayOfWeek = d.getDay();
      if (dayOfWeek >= 1 && dayOfWeek <= 5) { // Mo-Fr
        count++;
      }
    }
    
    return count;
  }
}

module.exports = new WorkdaysService();

