package org.synyx.urlaubsverwaltung.core.sync;

import microsoft.exchange.webservices.data.autodiscover.IAutodiscoverRedirectionUrl;
import microsoft.exchange.webservices.data.core.ExchangeService;
import microsoft.exchange.webservices.data.core.enumeration.property.WellKnownFolderName;
import microsoft.exchange.webservices.data.core.enumeration.service.SendInvitationsMode;
import microsoft.exchange.webservices.data.core.service.folder.CalendarFolder;
import microsoft.exchange.webservices.data.core.service.folder.Folder;
import microsoft.exchange.webservices.data.core.service.item.Appointment;
import microsoft.exchange.webservices.data.credential.WebCredentials;
import microsoft.exchange.webservices.data.search.FindFoldersResults;
import microsoft.exchange.webservices.data.search.FolderView;

import org.apache.log4j.Logger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Service;

import org.synyx.urlaubsverwaltung.core.mail.MailService;
import org.synyx.urlaubsverwaltung.core.person.Person;


/**
 * Provides sync of absences with exchange server calendar.
 *
 * @author  Daniel Hammann - <hammann@synyx.de>
 * @author  Aljona Murygina - murygina@synyx.de
 */
@Service("calendarSyncService")
public class ExchangeCalendarSyncService implements CalendarSyncService {

    private static final Logger LOG = Logger.getLogger(ExchangeCalendarSyncService.class);

    private MailService mailService;
    private ExchangeService exchangeService;

    private String calendarName;
    private CalendarFolder calendarFolder;

    @Autowired
    public ExchangeCalendarSyncService(MailService mailService,
        @Value("${ews.email}") String emailAddress,
        @Value("${ews.password}") String password,
        @Value("${ews.calendar}") String calendarName) {

        this.mailService = mailService;
        this.calendarName = calendarName;

        try {
            exchangeService = new ExchangeService();
            exchangeService.setCredentials(new WebCredentials(emailAddress, password));
            exchangeService.autodiscoverUrl(emailAddress, new RedirectionUrlCallback());
            exchangeService.setTraceEnabled(true);

            calendarFolder = findCalendar(calendarName);

            if (calendarFolder == null) {
                calendarFolder = createCalendar(calendarName);
            }
        } catch (Exception e) {
            // TODO
            e.printStackTrace();
        }
    }

    private CalendarFolder findCalendar(String searchedCalendarName) throws Exception {

        FindFoldersResults calendarRoot = exchangeService.findFolders(WellKnownFolderName.Calendar,
                new FolderView(Integer.MAX_VALUE));

        for (Folder folder : calendarRoot.getFolders()) {
            if (folder.getDisplayName().equals(searchedCalendarName)) {
                return (CalendarFolder) folder;
            }
        }

        return null;
    }


    private CalendarFolder createCalendar(String calendarName) throws Exception {

        CalendarFolder folder = new CalendarFolder(exchangeService);
        folder.setDisplayName(calendarName);
        folder.save(WellKnownFolderName.Calendar);

        LOG.info(String.format("New calendar folder '%s' created.", folder.getDisplayName()));

        return CalendarFolder.bind(exchangeService, folder.getId());
    }


    @Override
    public String addAbsence(Absence absence) {

        try {
            Appointment appointment = new Appointment(exchangeService);

            Person person = absence.getPerson();

            appointment.setSubject(String.format("Urlaub %s", person.getNiceName()));
            appointment.setStart(absence.getStartDate());
            appointment.setEnd(absence.getEndDate());
            appointment.setIsAllDayEvent(absence.isAllDay());
            appointment.getRequiredAttendees().add(person.getEmail());

            appointment.save(calendarFolder.getId(), SendInvitationsMode.SendToAllAndSaveCopy);

            LOG.info(String.format("Appointment %s for %s added to exchange calendar '%s'.", appointment.getId(),
                    person.getNiceName(), calendarFolder.getDisplayName()));

            return appointment.getId().getUniqueId();
        } catch (Exception ex) {
            mailService.sendCalendarSyncErrorNotification(calendarName, absence, ex.getMessage());
        }

        return null;
    }

    private class RedirectionUrlCallback implements IAutodiscoverRedirectionUrl {

        @Override
        public boolean autodiscoverRedirectionUrlValidationCallback(String redirectionUrl) {

            return redirectionUrl.toLowerCase().startsWith("https://");
        }
    }
}

@@ -1115,16 +1115,44 @@
 erson;%0A%0A
+import java.util.Optional;%0A%0A
 %0A/**%0A * 
@@ -2293,33 +2293,42 @@
 );%0A%0A            
-c
+Optional%3CC
 alendarFolder = 
@@ -2324,16 +2324,34 @@
 arFolder
+%3E calendarOptional
  = findC
@@ -2402,23 +2402,107 @@
 ndar
-Folder == null)
+Optional.isPresent()) %7B%0A                calendarFolder = calendarOptional.get();%0A            %7D else
  %7B%0A 
@@ -2683,32 +2683,41 @@
  %7D%0A%0A    private 
+Optional%3C
 CalendarFolder f
@@ -2714,16 +2714,17 @@
 arFolder
+%3E
  findCal
@@ -2736,25 +2736,17 @@
 (String 
-searchedC
+c
 alendarN
@@ -3032,17 +3032,9 @@
 als(
-searchedC
+c
 alen
@@ -3068,16 +3068,28 @@
  return 
+Optional.of(
 (Calenda
@@ -3103,16 +3103,17 @@
 ) folder
+)
 ;%0A      
@@ -3138,36 +3138,48 @@
 %0A        return 
-null
+Optional.empty()
 ;%0A    %7D%0A%0A%0A    pr
@@ -4278,18 +4278,20 @@
  %25s for 
+'
 %25s
+'
  added t

use optionals instead of null