package com.bytesw.bytebot.service;

import com.bytesw.bytebot.bean.CalendarBean;
import com.bytesw.bytebot.bean.CalendarExceptionBean;
import com.bytesw.bytebot.bean.WeekSchedulerBean;
import com.bytesw.bytebot.model.Calendar;
import com.bytesw.bytebot.model.CalendarException;
import com.bytesw.bytebot.model.SchedulerTask;
import com.bytesw.bytebot.model.WeekScheduler;
import com.bytesw.bytebot.repository.CalendarExceptionRepository;
import com.bytesw.bytebot.repository.CalendarRepository;
import com.bytesw.bytebot.repository.SchedulerTaskRepository;
import com.bytesw.bytebot.repository.WeekSchedulerRepository;
import com.bytesw.xdf.exception.AlreadyExistsException;
import com.bytesw.xdf.exception.NotFoundException;
import com.bytesw.xdf.exception.ReferentialIntegrityException;
import com.bytesw.xdf.service.XDFService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@Transactional
public class CalendarService extends XDFService<Calendar, CalendarBean, String> {

    @Autowired
    private WeekSchedulerService weekSchedulerService;

    @Autowired
    private WeekSchedulerRepository weekSchedulerRepository;

    @Autowired
    private SchedulerTaskRepository schedulerTaskRepository;

    @Autowired
    private CalendarExceptionRepository calendarExceptionRepository;

    @Autowired
    private CalendarRepository calendarRepository;

    @Autowired
    private CalendarExceptionService calendarExceptionService;

    protected CalendarService(CalendarRepository repository) {
        super(repository);
    }

    @Override
    protected Calendar toModel(Calendar model, CalendarBean bean) {
        if (model == null) {
            model = new Calendar();
        }
        BeanUtils.copyProperties(bean, model);
        return model;
    }

    @Override
    protected CalendarBean toBean(Calendar model) {
        CalendarBean bean = new CalendarBean();
        BeanUtils.copyProperties(model, bean);
        bean.setWeekSchedulerBeanList(this.weekSchedulerService.getWeekSchedulerByCalId(model.getId().trim()));
        /**/

        return bean;
    }

    @Override
    public void delete(String id) {
        Optional<Calendar> found = this.calendarRepository.findById(id);
        if (!found.isPresent()) {
            throw new NotFoundException();
        }
        Optional<List<SchedulerTask>> schedulerTaskList = this.schedulerTaskRepository.findByCalendarId(id);
        if (schedulerTaskList.isPresent()) {

            throw new ReferentialIntegrityException("CALENDAR_EXCEPTION_DELETE_SCHEDULER_TASK");
        }

        Optional<List<CalendarException>> calendarExceptionList = this.calendarExceptionRepository.findByCalendarId(id);
        calendarExceptionList.ifPresent(list -> this.calendarExceptionRepository.deleteAll(list));

        Optional<List<WeekScheduler>> weekSchedulerList = this.weekSchedulerRepository.findByCalendarId(id);
        weekSchedulerList.ifPresent(list -> this.weekSchedulerRepository.deleteAll(list));

        this.calendarRepository.delete(found.get());
    }

    @Override
    public CalendarBean create(CalendarBean bean) {
        if (this.calendarRepository.existsById(bean.getId())) {
            throw new AlreadyExistsException("errors.form.duplicated.id");
        }
        Calendar model = new Calendar();
        model = this.calendarRepository.save(this.toModel(model, bean));
        saveWeekScheduler(bean, model);
        saveCalendarException(bean, model);
        return this.toBean(model);
    }

    @Override
    public CalendarBean update(CalendarBean bean, String id) {
        Optional<Calendar> found = this.calendarRepository.findById(id);
        if (!found.isPresent()) {
            throw new NotFoundException();
        }
        Calendar calendar = this.toModel(found.get(), bean);
        BeanUtils.copyProperties(calendar, found.get());
        calendar = this.calendarRepository.save(found.get());
        saveWeekScheduler(bean, calendar);
        saveCalendarException(bean, calendar);
        return this.toBean(calendar);
    }

    private void saveWeekScheduler(CalendarBean bean, Calendar calendar) {
        List<WeekSchedulerBean> weekSchedulerBeanList = bean.getWeekSchedulerBeanList();
        Optional<List<WeekScheduler>> weekSchedulerBeanListBD = weekSchedulerRepository.findByCalendarId(calendar.getId().trim());
        List<BigInteger> weekIds = new ArrayList<>();

        for (WeekSchedulerBean weekSchedulerBean : weekSchedulerBeanList) {
            WeekScheduler weekScheduler;
            boolean delete = weekSchedulerBean.getFrom() == null;
            if (weekSchedulerBean.getId() != null) {
                Optional<WeekScheduler> weekSchedulerOptional = this.weekSchedulerRepository.findById(weekSchedulerBean.getId());
                if (weekSchedulerOptional.isPresent()) {
                    weekIds.add(weekSchedulerOptional.get().getId());
                    if (delete) {
                        this.weekSchedulerRepository.delete(weekSchedulerOptional.get());
                        continue;
                    }
                    weekScheduler = weekSchedulerOptional.get();
                } else {
                    weekScheduler = new WeekScheduler();
                    weekScheduler.setDayOfWeek(weekSchedulerBean.getDayOfWeek());
                }
//                weekScheduler.setCalendar(calendar);
                weekScheduler.setCalendarID(calendar.getId());
                weekScheduler.setFrom(weekSchedulerBean.getFrom());
                weekScheduler.setTo(weekSchedulerBean.getTo());
                this.weekSchedulerRepository.save(weekScheduler);
            } else if (!delete) {
                weekScheduler = new WeekScheduler();
//                weekScheduler.setCalendar(calendar);
                weekScheduler.setCalendarID(calendar.getId());
                weekScheduler.setDayOfWeek(weekSchedulerBean.getDayOfWeek());
                weekScheduler.setFrom(weekSchedulerBean.getFrom());
                weekScheduler.setTo(weekSchedulerBean.getTo());
                this.weekSchedulerRepository.save(weekScheduler);
            }

        }

        if (weekSchedulerBeanListBD.isPresent()){
            for (WeekScheduler weekScheduler: weekSchedulerBeanListBD.get()) {
                if (!weekIds.contains(weekScheduler.getId())) {
                        weekSchedulerRepository.deleteById(weekScheduler.getId());
                }
            }
        }
    }

    private void saveCalendarException(CalendarBean bean, Calendar calendar) {
        List<CalendarExceptionBean> calendarExceptionBeanList = bean.getCalendarExceptionBeanList();
        Optional<List<CalendarException>> calendarExceptionListBD = calendarExceptionRepository.findByCalendarId(calendar.getId().trim());
        List<BigInteger> exceptionIds = new ArrayList<>();

        CalendarException calendarException = null;
        for (CalendarExceptionBean calendarExceptionBean : calendarExceptionBeanList) {

            if (calendarExceptionBean.getId() != null) {
                Optional<CalendarException> calendarExceptionOptional = calendarExceptionRepository.findById(calendarExceptionBean.getId());
                if (calendarExceptionOptional.isPresent()) {
                    exceptionIds.add(calendarExceptionBean.getId());
                }
                if (calendarExceptionBean.isDelete()) {
                    calendarExceptionRepository.delete(calendarExceptionOptional.get());
                    continue;
                }

                // modificar
                if (!calendarExceptionOptional.get().getFrequencyType().equals(calendarExceptionBean.getFrequencyType())) {
                    calendarExceptionRepository.delete(calendarExceptionOptional.get());
                } else {
                    calendarExceptionBean.setCalendar(bean);
                    calendarException = calendarExceptionService.toModel(calendarExceptionOptional.get(), calendarExceptionBean);
                    calendarExceptionRepository.save(calendarException);
                    continue;
                }

            }

            // Nueva excepción
            calendarException = calendarExceptionService.getCalendarException(calendarExceptionBean);
            calendarExceptionBean.setCalendar(bean);
            calendarException = calendarExceptionService.toModel(calendarException, calendarExceptionBean);
            calendarException.setCalendar(calendar);
            calendarExceptionRepository.save(calendarException);
        }
        if (calendarExceptionListBD.isPresent()){
            for (CalendarException calException: calendarExceptionListBD.get()) {
                if (!exceptionIds.contains(calException.getId())) {
                    calendarExceptionRepository.deleteById(calException.getId());
                }
            }
        }
    }

}
