package de.jottyfan.camporganizer.module.business.bookings;

import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPPROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PERSON;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_PROFILE;
import static de.jottyfan.camporganizer.db.jooq.Tables.V_CAMP;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.Record10;
import org.jooq.Record13;
import org.jooq.SelectConditionStep;
import org.jooq.SelectSeekStep1;
import org.jooq.SelectSeekStep4;
import org.jooq.UpdateConditionStep;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import de.jottyfan.camporganizer.db.EnumConverter;
import de.jottyfan.camporganizer.db.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumModule;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.db.jooq.enums.EnumSex;
import de.jottyfan.camporganizer.db.jooq.tables.records.TPersonRecord;
import de.jottyfan.camporganizer.module.business.bookings.model.BookerBean;

/**
 *
 * @author jotty
 *
 */
@Repository
@Transactional(transactionManager = "transactionManager")
public class BookingsRepository {
	private static final Logger LOGGER = LogManager.getLogger(BookingsRepository.class);

	@Autowired
	private DSLContext jooq;

	/**
	 * get a list of all registered persons that have booked the camp
	 *
	 * @param username the name of the current user in this session
	 * @param year the year of the camp; may be null
	 *
	 * @return a list of bookings; an empty one at least
	 */
	public List<BookerBean> getBookings(String username, Integer year) {
		SelectSeekStep4<Record10<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double>, EnumCamprole, EnumSex, String, String> sql = jooq
		// @formatter:off
		  .select(T_PERSON.PK, T_PERSON.PROGRESS, T_PERSON.PAID, T_PERSON.FORENAME, T_PERSON.SURNAME, T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.CREATED, V_CAMP.NAME, V_CAMP.YEAR)
		  .from(T_PERSON)
		  .leftJoin(V_CAMP).on(V_CAMP.PK.eq(T_PERSON.FK_CAMP))
			.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP)).and(T_CAMPPROFILE.MODULE.eq(EnumModule.business))
			.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
		  .where(T_PROFILE.USERNAME.eq(username))
		  .and(year != null ? V_CAMP.YEAR.eq(year.doubleValue()) : DSL.trueCondition())
		  .orderBy(T_PERSON.CAMPROLE, T_PERSON.SEX, T_PERSON.SURNAME, T_PERSON.FORENAME);
		// @formatter:on
		LOGGER.trace(sql);
		List<BookerBean> list = new ArrayList<>();
		Iterator<Record10<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double>> i = sql.fetch().iterator();
		while (i.hasNext()) {
			Record10<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, String, Double> r = i.next();
			String forename = r.get(T_PERSON.FORENAME);
			String surname = r.get(T_PERSON.SURNAME);
			EnumCamprole role = r.get(T_PERSON.CAMPROLE);
			EnumSex sex = r.get(T_PERSON.SEX);
			EnumProgress progress = r.get(T_PERSON.PROGRESS);
			String campName = r.get(V_CAMP.NAME);
			Double campYear = r.get(V_CAMP.YEAR);
			BookerBean bean = new BookerBean();
			bean.setPk(r.get(T_PERSON.PK));
			bean.setName(String.format("%s %s", forename, surname));
			bean.setRole(EnumConverter.role2GermanNames(role));
			bean.setSex(EnumConverter.sex2GermanNames(sex));
			bean.setBookingDate(r.get(T_PERSON.CREATED));
			bean.setProgress(progress == null ? null : progress.getLiteral());
			bean.setPaid(r.get(T_PERSON.PAID));
			bean.setCamp(String.format("%s %4.0f", campName, campYear));
			list.add(bean);
		}
		return list;
	}

	/**
	 * get the booking payment information of the user referenced by id
	 *
	 * @param id       the id of the user
	 * @param username the name of the current's session user
	 * @return the booker bean or null
	 */
	public BookerBean getBooking(Integer id, String username) {
		SelectConditionStep<Record13<Integer, EnumProgress, BigDecimal, String, String, EnumCamprole, EnumSex, LocalDateTime, BigDecimal, String, Double, String, Integer>> sql = jooq
		// @formatter:off
		  .select(T_PERSON.PK,
		  		    T_PERSON.PROGRESS,
		  		    T_PERSON.PAID,
		  		    T_PERSON.FORENAME,
		  		    T_PERSON.SURNAME,
		  		    T_PERSON.CAMPROLE,
		  		    T_PERSON.SEX,
		  		    T_PERSON.CREATED,
		  		    T_PERSON.REQUIRED_PRICE,
		  		    V_CAMP.NAME,
		  		    V_CAMP.YEAR,
		  		    V_CAMP.PRICE,
		  		    V_CAMP.PK)
		  .from(T_PERSON)
		  .leftJoin(V_CAMP).on(V_CAMP.PK.eq(T_PERSON.FK_CAMP))
			.leftJoin(T_CAMPPROFILE).on(T_CAMPPROFILE.FK_CAMP.eq(T_PERSON.FK_CAMP)).and(T_CAMPPROFILE.MODULE.eq(EnumModule.business))
			.leftJoin(T_PROFILE).on(T_PROFILE.PK.eq(T_CAMPPROFILE.FK_PROFILE))
		  .where(T_PROFILE.USERNAME.eq(username))
		  .and(T_PERSON.PK.eq(id));
		// @formatter:on
		LOGGER.trace(sql);
		Record r = sql.fetchOne();
		if (r == null) {
			return null;
		} else {
			String forename = r.get(T_PERSON.FORENAME);
			String surname = r.get(T_PERSON.SURNAME);
			EnumCamprole role = r.get(T_PERSON.CAMPROLE);
			EnumSex sex = r.get(T_PERSON.SEX);
			EnumProgress progress = r.get(T_PERSON.PROGRESS);
			BigDecimal requiredPrice = r.get(T_PERSON.REQUIRED_PRICE);
			String campName = r.get(V_CAMP.NAME);
			Double campYear = r.get(V_CAMP.YEAR);
			Integer campId = r.get(V_CAMP.PK);
			BookerBean bean = new BookerBean();
			bean.setPk(r.get(T_PERSON.PK));
			bean.setName(String.format("%s %s", forename, surname));
			bean.setRole(EnumConverter.role2GermanNames(role));
			bean.setSex(EnumConverter.sex2GermanNames(sex));
			bean.setBookingDate(r.get(T_PERSON.CREATED));
			bean.setProgress(progress == null ? null : progress.getLiteral());
			bean.setPaid(r.get(T_PERSON.PAID));
			bean.setCamp(String.format("%s %4.0f", campName, campYear));
			bean.setCampId(campId);
			bean.setPrice(r.get(V_CAMP.PRICE));
			bean.setRequiredPrice(requiredPrice);
			return bean;
		}
	}

	/**
	 * add payment for user referenced by pk
	 *
	 * @param pk      the ID of the user
	 * @param payment the payment (additional)
	 * @return the number of affected database rows, should be 1
	 */
	public Integer addPayment(Integer pk, Double payment) {
		BigDecimal oldValue = jooq.select(T_PERSON.PAID).from(T_PERSON).where(T_PERSON.PK.eq(pk)).fetchOne().get(T_PERSON.PAID);
		BigDecimal value = oldValue == null ? BigDecimal.valueOf(payment) : oldValue.add(BigDecimal.valueOf(payment));
		UpdateConditionStep<TPersonRecord> sql = jooq
		// @formatter:off
			.update(T_PERSON)
			.set(T_PERSON.PAID, value)
			.where(T_PERSON.PK.eq(pk));
		// @formatter:on
		LOGGER.trace(sql);
		return sql.execute();
	}

	/**
	 * get an ordered list of all camp years found in the database
	 *
	 * @return the list of camp years
	 */
	public List<Integer> getAllCampYears() {
		SelectSeekStep1<Record1<Double>, Double> sql = jooq
		// @formatter:off
			.selectDistinct(V_CAMP.YEAR)
			.from(V_CAMP)
			.orderBy(V_CAMP.YEAR);
		// @formatter:on
		LOGGER.trace(sql);
		return sql.fetchInto(Integer.class);
	}
}
