package org.synyx.urlaubsverwaltung.calendar;

import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.component.VEvent;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.Uid;
import net.fortuna.ical4j.model.property.XProperty;
import net.fortuna.ical4j.validate.ValidationException;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Service;
import org.synyx.urlaubsverwaltung.absence.Absence;

import java.time.ZonedDateTime;
import java.util.List;

import static java.util.Date.from;
import static java.util.stream.Collectors.toList;
import static net.fortuna.ical4j.model.property.CalScale.GREGORIAN;
import static net.fortuna.ical4j.model.property.Version.VERSION_2_0;


@Service
class ICalService {

    String generateCalendar(String title, List<Absence> absences) {

        final Calendar calendar = new Calendar();
        calendar.getProperties().add(VERSION_2_0);
        calendar.getProperties().add(new ProdId("-//Urlaubsverwaltung//iCal4j 1.0//DE"));
        calendar.getProperties().add(GREGORIAN);
        calendar.getProperties().add(new XProperty("X-WR-CALNAME", title));
        calendar.getProperties().add(new XProperty("X-MICROSOFT-CALSCALE", GREGORIAN.getValue()));

        final List<VEvent> absencesVEvents = absences.stream().map(this::toVEvent).collect(toList());
        calendar.getComponents().addAll(absencesVEvents);

        try {
            calendar.validate();
        } catch (ValidationException e) {
            throw new CalendarException("Validation does not pass", e);
        }

        return calendar.toString();
    }


    private VEvent toVEvent(Absence absence) {


        final ZonedDateTime startDateTime = absence.getStartDate();
        final ZonedDateTime endDateTime = absence.getEndDate();

        final DateTime start = new DateTime(from(startDateTime.toInstant()));
        start.setUtc(true);
        final DateTime end = new DateTime(from(endDateTime.toInstant()));
        end.setUtc(true);

        final VEvent event;
        if (absence.isAllDay()) {
            if (isSameDay(startDateTime, endDateTime)) {
                event = new VEvent(new Date(start.getTime()), absence.getEventSubject());
            } else {
                event = new VEvent(new Date(start), new Date(end), absence.getEventSubject());
            }

            event.getProperties().add(new XProperty("X-MICROSOFT-CDO-ALLDAYEVENT", "TRUE"));
        } else {
            event = new VEvent(start, end, absence.getEventSubject());
        }

        event.getProperties().add(new Uid(generateUid(absence)));

        return event;
    }

    private boolean isSameDay(ZonedDateTime startDateTime, ZonedDateTime endDate) {
        return startDateTime.toLocalDate().isEqual(endDate.toLocalDate().minusDays(1));
    }

    private String generateUid(Absence absence) {
        final String data = absence.getStartDate() + "" + absence.getEndDate() + "" + absence.getPerson();
        return DigestUtils.md5Hex(data).toUpperCase();
    }
}

@@ -40,16 +40,66 @@
 endar;%0A%0A
+import net.fortuna.ical4j.data.CalendarOutputter;%0A
 import n
@@ -617,16 +617,73 @@
 sence;%0A%0A
+import java.io.IOException;%0Aimport java.io.StringWriter;%0A
 import j
@@ -1642,44 +1642,216 @@
     
-try %7B%0A            calendar.validate(
+final StringWriter calenderWriter = new StringWriter();%0A        final CalendarOutputter calendarOutputter = new CalendarOutputter();%0A        try %7B%0A            calendarOutputter.output(calendar, calenderWriter
 );%0A 
@@ -1886,16 +1886,30 @@
 ception 
+%7C IOException 
 e) %7B%0A   
@@ -1950,32 +1950,44 @@
 on(%22
-Validation does not pass
+iCal calender could not be generated
 %22, e
@@ -2021,17 +2021,23 @@
 n calend
-a
+erWrite
 r.toStri

Use ica4l outputter to be respect the compatibility hints