package de.champonthis.buntspecht.businesslogic;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.QueryResults;
import com.querydsl.core.Tuple;
import com.querydsl.core.types.Order;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;

import de.champonthis.buntspecht.controller.model.TurnoverFilterModel;
import de.champonthis.buntspecht.model.QTurnover;
import de.champonthis.buntspecht.model.Turnover;
import de.champonthis.buntspecht.repository.TurnoverRepository;

@Service
public class TurnoverManager {

	@Autowired
	private TurnoverRepository turnoverRepository;
	@Autowired
	private JPAQueryFactory jpaQueryFactory;

	private QTurnover qTurnover = QTurnover.turnover;

	public QueryResults<Turnover> fetch(long limit, long offset, String sortBy, boolean descending,
			TurnoverFilterModel filter) {
		return fetch(null, limit, offset, sortBy, descending, filter);
	}

	public QueryResults<Turnover> fetch(String username, long limit, long offset, String sortBy, boolean descending,
			TurnoverFilterModel filter) {
		BooleanBuilder builder = new BooleanBuilder();

		if (StringUtils.hasText(username)) {
			builder.and(qTurnover.username.eq(username));
		}

		builder.and(buildFilter(filter));

		JPAQuery<Turnover> query = jpaQueryFactory.from(qTurnover).where(builder.getValue()).select(qTurnover);
		Long total = query.clone().select(qTurnover.id.countDistinct()).fetchOne();

		if (StringUtils.hasText(sortBy)) {
			Path<? extends Comparable<?>> path = null;
			switch (sortBy) {
				case "created":
					path = qTurnover.created;
					break;
				case "updated":
					path = qTurnover.updated;
					break;
				case "customer":
					path = qTurnover.customer;
					break;
				case "price":
					path = qTurnover.price;
					break;
				case "timeInvestment":
					path = qTurnover.timeInvestment;
					break;
			}
			if (path != null) {
				query.orderBy(new OrderSpecifier<>(descending ? Order.DESC : Order.ASC, path));
			}
		}

		List<Turnover> result = query.limit(limit).offset(offset).fetch();
		return new QueryResults<Turnover>(result, limit, offset, total == null ? 0L : total);

	}

	protected Predicate buildFilter(TurnoverFilterModel filter) {
		BooleanBuilder builder = new BooleanBuilder();

		if (filter != null) {
			if (filter.getCreated() != null) {
				if (filter.getCreated().getMin() != null) {
					builder.and(qTurnover.created.after(filter.getCreated().getMin()));
				}
				if (filter.getCreated().getMax() != null) {
					builder.and(qTurnover.created.before(filter.getCreated().getMax()));
				}
			}
			if (filter.getUpdated() != null) {
				if (filter.getUpdated().getMin() != null) {
					builder.and(qTurnover.updated.after(filter.getUpdated().getMin()));
				}
				if (filter.getUpdated().getMax() != null) {
					builder.and(qTurnover.updated.before(filter.getUpdated().getMax()));
				}
			}
			if (filter.getCustomer() != null) {
				builder.and(qTurnover.customer.contains(filter.getCustomer()));
			}
			if (filter.getMotif() != null) {
				builder.and(qTurnover.motif.contains(filter.getMotif()));
			}
			if (filter.getPrice() != null) {
				if (filter.getPrice().getMin() != null) {
					builder.and(qTurnover.price.goe(filter.getPrice().getMin()));
				}
				if (filter.getPrice().getMax() != null) {
					builder.and(qTurnover.price.loe(filter.getPrice().getMax()));
				}
			}
			if (filter.getTimeInvestment() != null) {
				if (filter.getTimeInvestment().getMin() != null) {
					builder.and(qTurnover.timeInvestment.goe(filter.getTimeInvestment().getMin()));
				}
				if (filter.getTimeInvestment().getMax() != null) {
					builder.and(qTurnover.timeInvestment.loe(filter.getTimeInvestment().getMax()));
				}
			}
		}

		return builder.getValue();
	}

	public Turnover get(Long id) {
		return turnoverRepository.findById(id).orElse(null);
	}

	public Turnover save(Turnover turnover) {
		return turnoverRepository.save(turnover);
	}

	public boolean exists(Long id) {
		return turnoverRepository.existsById(id);
	}

	public void delete(Turnover turnover) {
		turnoverRepository.delete(turnover);
	}

	public void deleteById(Long id) {
		turnoverRepository.deleteById(id);
	}

	public void deleteByUsername(String username) {
		turnoverRepository.deleteAllInBatch(turnoverRepository.findAll(qTurnover.username.eq(username)));
	}

	public QueryResults<Tuple> overview(String username, long limit, long offset, String sortBy, boolean descending,
			TurnoverFilterModel filter) {
		BooleanBuilder builder = new BooleanBuilder();

		if (StringUtils.hasText(username)) {
			builder.and(qTurnover.username.eq(username));
		}

		builder.and(buildFilter(filter));

		JPAQuery<Tuple> query = jpaQueryFactory.from(qTurnover).where(builder.getValue()).groupBy(qTurnover.username)
				.select(qTurnover.username.as("username"), qTurnover.price.sum().as("price"),
						qTurnover.timeInvestment.sum().as("timeInvestment"));
		Long total = query.clone().select(qTurnover.username.countDistinct()).fetchOne();

		if (StringUtils.hasText(sortBy)) {
			Path<? extends Comparable<?>> path = null;
			switch (sortBy) {
				case "username":
					path = qTurnover.username;
					break;
				case "price":
					path = qTurnover.price;
					break;
				case "timeInvestment":
					path = qTurnover.timeInvestment;
					break;
			}
			if (path != null) {
				query.orderBy(new OrderSpecifier<>(descending ? Order.DESC : Order.ASC, path));
			}
		}

		List<Tuple> result = query.limit(limit).offset(offset)
				.fetch();

		return new QueryResults<Tuple>(result, limit, offset, total == null ? 0L : total);
	}

}
