import { Op } from 'sequelize';
import TrainingCancellation from '../models/TrainingCancellation.js';
import { checkAccess } from '../utils/userUtils.js';
import HttpError from '../exceptions/HttpError.js';

class TrainingCancellationService {
  async getTrainingCancellations(userToken, clubId, year) {
    await checkAccess(userToken, clubId);
    const normalizedYear = this.normalizeYear(year);
    return await TrainingCancellation.findAll({
      where: {
        clubId,
        [Op.or]: [
          {
            startDate: { [Op.lte]: `${normalizedYear}-12-31` },
            endDate: { [Op.gte]: `${normalizedYear}-01-01` },
          },
          {
            date: { [Op.between]: [`${normalizedYear}-01-01`, `${normalizedYear}-12-31`] },
          },
        ],
      },
      order: [['startDate', 'ASC'], ['date', 'ASC']],
    });
  }

  async upsertTrainingCancellation(userToken, clubId, date, reason, endDate = null, trainingGroupIds = []) {
    await checkAccess(userToken, clubId);
    const normalizedStartDate = this.normalizeDate(date);
    const normalizedEndDate = this.normalizeDate(endDate || date);
    const normalizedTrainingGroupIds = this.normalizeTrainingGroupIds(trainingGroupIds);
    if (!normalizedStartDate || !normalizedEndDate) {
      throw new HttpError('Ungültiges Datum', 400);
    }
    if (normalizedStartDate > normalizedEndDate) {
      throw new HttpError('Enddatum darf nicht vor dem Startdatum liegen', 400);
    }

    const [cancellation] = await TrainingCancellation.upsert({
      clubId,
      startDate: normalizedStartDate,
      endDate: normalizedEndDate,
      date: normalizedStartDate,
      reason: String(reason || '').trim() || null,
      trainingGroupIds: normalizedTrainingGroupIds,
    });
    return cancellation || await TrainingCancellation.findOne({
      where: { clubId, startDate: normalizedStartDate, endDate: normalizedEndDate },
    });
  }

  async deleteTrainingCancellation(userToken, clubId, cancellationId) {
    await checkAccess(userToken, clubId);
    const cancellation = await TrainingCancellation.findOne({
      where: { id: cancellationId, clubId },
    });
    if (!cancellation) {
      throw new HttpError('Trainingsausfall nicht gefunden', 404);
    }
    await cancellation.destroy();
    return { success: true };
  }

  normalizeYear(year) {
    const parsed = Number.parseInt(year, 10);
    if (Number.isInteger(parsed) && parsed >= 2020 && parsed <= 2100) {
      return parsed;
    }
    return new Date().getFullYear();
  }

  normalizeDate(date) {
    const text = String(date || '').slice(0, 10);
    return /^\d{4}-\d{2}-\d{2}$/.test(text) ? text : null;
  }

  normalizeTrainingGroupIds(trainingGroupIds) {
    const values = this.parseTrainingGroupIdsValue(trainingGroupIds);
    return [...new Set(
      values
        .map(id => Number.parseInt(id, 10))
        .filter(id => Number.isInteger(id) && id > 0)
    )];
  }

  parseTrainingGroupIdsValue(trainingGroupIds) {
    if (Array.isArray(trainingGroupIds)) return trainingGroupIds;
    if (trainingGroupIds === null || trainingGroupIds === undefined || trainingGroupIds === '') return [];
    if (typeof trainingGroupIds === 'string') {
      try {
        const parsed = JSON.parse(trainingGroupIds);
        if (Array.isArray(parsed)) return parsed;
        if (parsed !== null && parsed !== undefined) return [parsed];
      } catch (error) {
        return trainingGroupIds.split(',');
      }
    }
    return [];
  }
}

export default new TrainingCancellationService();
