package com.bytesw.bytebot.service;

import com.bytesw.bytebot.bean.CalendarExceptionBean;
import com.bytesw.bytebot.model.*;
import com.bytesw.bytebot.model.Calendar;
import com.bytesw.bytebot.model.enums.CalendarExceptionType;
import com.bytesw.bytebot.repository.CalendarExceptionRepository;
import com.bytesw.bytebot.repository.CalendarRepository;
import com.bytesw.xdf.dao.rsql.CustomRsqlVisitor;
import com.bytesw.xdf.exception.NotFoundException;
import com.bytesw.xdf.service.XDFService;
import cz.jirutka.rsql.parser.RSQLParser;
import cz.jirutka.rsql.parser.ast.Node;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigInteger;
import java.util.*;

@Service
@Transactional
public class CalendarExceptionService extends XDFService<CalendarException, CalendarExceptionBean, BigInteger> {

    @Autowired
    CalendarRepository calendarRepository;

    @Autowired
    CalendarService calendarService;

    @Autowired
    CalendarExceptionRepository calendarExceptionRepository;

    protected CalendarExceptionService(CalendarExceptionRepository repository) {
        super(repository);
    }

    @Override
    protected CalendarException toModel(CalendarException model, CalendarExceptionBean bean) {
        if (model == null) {
            model = getCalendarException(bean);
        }

        BeanUtils.copyProperties(bean, model);
        if (bean.getCalendar() == null) {
            throw new NotFoundException("Calendar can not be null");
        }
        Optional<Calendar> calendarOptional = this.calendarRepository.findById(bean.getCalendar().getId());
        if (!calendarOptional.isPresent()) {
            throw new NotFoundException("Calendar not found " + bean.getCalendar().getId());
        }

        if (bean.getCalendarExceptionType() == null) {
            throw new NotFoundException("Calendar Exception Type can not be null");
        }

        model.setCalendar(calendarOptional.get());
        model.setCalendarExceptionType(CalendarExceptionType.fromString(bean.getCalendarExceptionType()));
        return model;
    }

    public static CalendarException getCalendarException(CalendarExceptionBean bean) {
        CalendarException model;
        switch (bean.getFrequencyType()) {
            case UniqueCalendarException.DISCRIMINATOR_VALUE:
                model = new UniqueCalendarException();
                break;
            case AnnualCalendarException.DISCRIMINATOR_VALUE:
                model = new AnnualCalendarException();
                break;
            case RangeCalendarException.DISCRIMINATOR_VALUE:
                model = new RangeCalendarException();
                break;
            default:
                model = new UniqueWeeklyCalendarException();
        }
        return model;
    }

    @Override
    protected CalendarExceptionBean toBean(CalendarException model) {
        CalendarExceptionBean bean = new CalendarExceptionBean();
        BeanUtils.copyProperties(model, bean);
        bean.setCalendar(this.calendarService.toBean(model.getCalendar()));
        bean.setCalendarExceptionType(model.getCalendarExceptionType().getName());
        bean.setFrequencyType(model.getFrequencyType());
        return bean;
    }

    public List<CalendarExceptionBean> getCalendarExceptionbyID(Map<String, String> parametersCalendar) {
        String sortDirection = parametersCalendar.get("sortDirection");
        String columnName = parametersCalendar.get("columnName");
        String caleId = parametersCalendar.get("caleId");
        String filterExpresion = parametersCalendar.get("filterExpresion");
        Specification<CalendarException> calendarExceptionSpecification = null;
        Sort sort;
        switch (sortDirection) {
            case "desc":
                sort = Sort.by(Sort.Direction.DESC, columnName);
                break;
            case "asc":
                sort = Sort.by(Sort.Direction.ASC, columnName);
                break;
            default:
                sort = Sort.by(Sort.Direction.ASC, "description");
        }

        if (filterExpresion != null) {
            if (!filterExpresion.isEmpty()) {
                filterExpresion = filterExpresion + ";";
            }
            filterExpresion = filterExpresion + String.format("calendar.id=='%s'", caleId);
            calendarExceptionSpecification = createSpecification(filterExpresion);
        }
        List<CalendarException> optionalCalendarExceptionList = calendarExceptionRepository.findAll(calendarExceptionSpecification, sort);
        List<CalendarExceptionBean> calendarExceptionBeanList = new ArrayList<>();
        for (int i = 0; i < optionalCalendarExceptionList.size(); i++) {
            CalendarExceptionBean aux = new CalendarExceptionBean();
            BeanUtils.copyProperties(optionalCalendarExceptionList.get(i), aux);

            calendarExceptionBeanList.add(i, toBean(optionalCalendarExceptionList.get(i)));
        }
        return calendarExceptionBeanList;
    }

    private Specification<CalendarException> createSpecification(String filterExpression) {
        Node rootNode = (new RSQLParser()).parse(filterExpression);
        return (Specification) rootNode.accept(new CustomRsqlVisitor<>());
    }


}
