package de.jottyfan.camporganizer.module.camplist;

import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMP;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_CAMPDOCUMENT;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENT;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_DOCUMENTROLE;
import static de.jottyfan.camporganizer.db.jooq.Tables.T_LOCATION;
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.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Stream;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jooq.Condition;
import org.jooq.DSLContext;
import org.jooq.Record;
import org.jooq.Record2;
import org.jooq.SelectOrderByStep;
import org.jooq.SelectSeekStep1;
import org.jooq.SelectSeekStep2;
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.jooq.enums.EnumCamprole;
import de.jottyfan.camporganizer.db.jooq.enums.EnumDocument;
import de.jottyfan.camporganizer.db.jooq.enums.EnumProgress;
import de.jottyfan.camporganizer.db.jooq.tables.TProfile;
import de.jottyfan.camporganizer.db.jooq.tables.records.VCampRecord;
import de.jottyfan.camporganizer.module.camplist.model.BookingBean;
import de.jottyfan.camporganizer.module.camplist.model.DocumentBean;

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

	@Autowired
	private DSLContext jooq;

	public Stream<VCampRecord> getAllCamps(Condition condition) {
		SelectSeekStep1<VCampRecord, LocalDateTime> sql = jooq.selectFrom(V_CAMP).where(condition).orderBy(V_CAMP.ARRIVE);
		LOGGER.trace(sql);
		return sql.fetchStream();
	}

	public List<BookingBean> getAllBookings(String username) {
		TProfile REGISTRATOR = TProfile.T_PROFILE.as("registrator");
		SelectSeekStep2<Record, LocalDateTime, LocalDateTime> sql = jooq
		// @formatter:off
			.select(T_PERSON.PK,
							T_PERSON.FORENAME,
					    T_PERSON.SURNAME,
					    T_PERSON.STREET,
					    T_PERSON.ZIP,
					    T_PERSON.CITY,
					    T_PERSON.PHONE,
					    T_PERSON.BIRTHDATE,
					    T_PERSON.CAMPROLE,
					    T_PERSON.COMMENT,
					    T_PERSON.CONSENT_CATALOG_PHOTO,
					    T_PERSON.CREATED,
					    T_PERSON.EMAIL,
					    T_PERSON.SEX,
					    T_PERSON.PROGRESS,
					    T_PERSON.FK_CAMP,
					    T_PROFILE.FORENAME,
					    T_PROFILE.SURNAME,
					    REGISTRATOR.FORENAME,
					    REGISTRATOR.SURNAME,
					    V_CAMP.NAME,
					    V_CAMP.LOCATION_NAME,
					    V_CAMP.PRICE,
					    V_CAMP.YEAR,
					    V_CAMP.ARRIVE,
					    V_CAMP.DEPART,
					    V_CAMP.COUNTRIES,
					    V_CAMP.URL,
					    V_CAMP.IS_OVER)
			.from(T_PROFILE)
			.leftJoin(T_PERSON).on(T_PERSON.FK_PROFILE.eq(T_PROFILE.PK))
			.leftJoin(REGISTRATOR).on(REGISTRATOR.PK.eq(T_PERSON.FK_REGISTRATOR))
			.leftJoin(V_CAMP).on(V_CAMP.PK.eq(T_PERSON.FK_CAMP))
			.where(DSL.trim(T_PROFILE.USERNAME).eq(username == null ? null : username.trim()))
			.and(T_PERSON.PK.isNotNull())
			.orderBy(V_CAMP.ARRIVE.desc(), T_PERSON.CREATED);
		// @formatter:on
		LOGGER.trace(sql);
		List<BookingBean> list = new ArrayList<>();
		Iterator<Record> i = sql.fetch().iterator();
		while (i.hasNext()) {
			Record r = i.next();
			Integer fkCamp = r.get(T_PERSON.FK_CAMP);
			BookingBean bean = new BookingBean();
			EnumProgress progress = r.get(T_PERSON.PROGRESS);
			bean.setProgress(progress == null ? null : progress.getLiteral());
			bean.setPk(r.get(T_PERSON.PK));
			bean.setForename(r.get(T_PERSON.FORENAME));
			bean.setSurname(r.get(T_PERSON.SURNAME));
			bean.setStreet(r.get(T_PERSON.STREET));
			bean.setZip(r.get(T_PERSON.ZIP));
			bean.setCity(r.get(T_PERSON.CITY));
			bean.setPhone(r.get(T_PERSON.PHONE));
			bean.setBirthdate(r.get(T_PERSON.BIRTHDATE));
			bean.setCamprole(r.get(T_PERSON.CAMPROLE));
			bean.setComment(r.get(T_PERSON.COMMENT));
			bean.setConsentCatalogPhoto(r.get(T_PERSON.CONSENT_CATALOG_PHOTO));
			bean.setCreated(r.get(T_PERSON.CREATED));
			bean.setEmail(r.get(T_PERSON.EMAIL));
			bean.setSex(r.get(T_PERSON.SEX));
			bean.setLocationName(r.get(V_CAMP.LOCATION_NAME));
			bean.setPrice(r.get(V_CAMP.PRICE));
			bean.setYear(r.get(V_CAMP.YEAR));
			bean.setArrive(r.get(V_CAMP.ARRIVE));
			bean.setDepart(r.get(V_CAMP.DEPART));
			bean.setCountries(r.get(V_CAMP.COUNTRIES));
			bean.setUrl(r.get(V_CAMP.URL));
			bean.setIsOver(r.get(V_CAMP.IS_OVER));
			bean.setCampName(r.get(V_CAMP.NAME));
			StringBuilder buf = new StringBuilder();
			String forename = r.get(REGISTRATOR.FORENAME);
			String surname = r.get(REGISTRATOR.SURNAME);
			if (forename != null) {
				buf.append(forename).append(" ");
			}
			buf.append(surname != null ? surname : "");
			bean.setRegistrator(buf.toString());
			buf = new StringBuilder();
			String regForename = r.get(T_PROFILE.FORENAME);
			String regSurname = r.get(T_PROFILE.SURNAME);
			if (regForename != null) {
				buf.append(regForename).append(" ");
			}
			buf.append(regSurname != null ? regSurname : "");
			bean.setSubscriber(buf.toString());
			bean.getDocuments().addAll(getAllDocumentBeans(fkCamp, bean.getCamprole()));
			list.add(bean);
		}
		return list;
	}

	private List<DocumentBean> getAllDocumentBeans(Integer fkCamp, EnumCamprole camprole) {
		SelectOrderByStep<Record2<String, Integer>> sql = jooq
		// @formatter:off
			.select(T_DOCUMENT.NAME,
					    T_DOCUMENT.PK)
			.from(T_CAMPDOCUMENT)
			.leftJoin(T_DOCUMENT).on(T_DOCUMENT.PK.eq(T_CAMPDOCUMENT.FK_DOCUMENT))
			.leftJoin(T_DOCUMENTROLE).on(T_DOCUMENTROLE.FK_DOCUMENT.eq(T_DOCUMENT.PK))
			.where(T_CAMPDOCUMENT.FK_CAMP.eq(fkCamp))
			.and(T_DOCUMENTROLE.CAMPROLE.eq(camprole))
			.unionAll(jooq
			  .select(T_DOCUMENT.NAME,
			  		    T_DOCUMENT.PK)
			  .from(T_CAMP)
			  .leftJoin(T_DOCUMENT).on(T_DOCUMENT.PK.eq(T_CAMP.FK_DOCUMENT))
				.leftJoin(T_DOCUMENTROLE).on(T_DOCUMENTROLE.FK_DOCUMENT.eq(T_DOCUMENT.PK))
			  .where(T_CAMP.PK.eq(fkCamp))
				.and(T_DOCUMENTROLE.CAMPROLE.eq(camprole))
			).unionAll(jooq
			  .select(T_DOCUMENT.NAME,
			  				T_DOCUMENT.PK)
			  .from(T_LOCATION)
			  .leftJoin(T_DOCUMENT).on(T_DOCUMENT.PK.eq(T_LOCATION.FK_DOCUMENT))
			  .leftJoin(T_DOCUMENTROLE).on(T_DOCUMENTROLE.FK_DOCUMENT.eq(T_DOCUMENT.PK))
			  .leftJoin(T_CAMP).on(T_CAMP.FK_LOCATION.eq(T_LOCATION.PK))
			  .where(T_CAMP.PK.eq(fkCamp))
				.and(T_DOCUMENTROLE.CAMPROLE.eq(camprole))
			).unionAll(jooq
				.select(T_DOCUMENT.NAME,
						    T_DOCUMENT.PK)
				.from(T_DOCUMENT)
				.where(T_DOCUMENT.DOCTYPE.eq(EnumDocument.camppass)));
		// @formatter:on
		LOGGER.trace(sql);
		Map<Integer, String> map = new HashMap<>(); // no duplicate on using a map
		Iterator<Record2<String, Integer>> i = sql.fetch().iterator();
		while (i.hasNext()) {
			Record2<String, Integer> r = i.next();
			map.put(r.get(T_DOCUMENT.PK), r.get(T_DOCUMENT.NAME));
		}
		List<DocumentBean> list = new ArrayList<>();
		for (Entry<Integer, String> entry : map.entrySet()) {
			DocumentBean bean = new DocumentBean();
			bean.setPk(entry.getKey());
			bean.setName(entry.getValue());
			list.add(bean);
		}
		return list;
	}
}
