package com.app.services;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.Period;
import java.util.List;
import java.util.Map;

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

import com.app.assemblers.PurchaseOrderResourceAssembler;
import com.app.exceptions.ConferenceNotAvailableException;
import com.app.exceptions.PONotFoundException;
import com.app.model.Conference;
import com.app.model.PurchaseOrder;
import com.app.model.Status;
import com.app.repository.ConferenceRepository;
import com.app.repository.PurchaseOrderRepository;
import com.app.resource.PurchaseOrderResource;
import com.fasterxml.jackson.databind.ObjectMapper;

import net.hamnaberg.json.Property;
import util.CollectionParserHelper;
import util.interfaces.BaseService;

@Service
public class PurchaseOrderService implements BaseService<Long, String> {

	@Autowired
	ConferenceRepository conferenceRepo;

	@Autowired
	PurchaseOrderRepository poRepo;

	public static PurchaseOrderResourceAssembler purchaseOrderResourceAssembler = new PurchaseOrderResourceAssembler();

	public PurchaseOrder acceptPurchaseOrder(Long id) {
		PurchaseOrder po = poRepo.findOne(id);
		po.setStatus(Status.APPROVED);
		poRepo.save(po);
		return po;
	}

	public static int getDateDifference(LocalDate localDate,
			LocalDate localDate2) {
		return Period.between(localDate, localDate2).getDays();
	}

	public PurchaseOrder acceptPurchaseOrderUpdate(Long id, Long upId) {
		PurchaseOrder po = poRepo.findOne(id);
		PurchaseOrder updatePo = poRepo.findOne(upId);

		po.setStartDate(updatePo.getStartDate());
		po.setEndDate(updatePo.getEndDate());
		po.setConference(updatePo.getConference());
		BigDecimal difference = new BigDecimal(
				getDateDifference(po.getStartDate(), po.getEndDate()));
		BigDecimal cost = updatePo.getConference().getPrice()
				.multiply(difference);
		po.setCost(cost);
		po.setStatus(Status.APPROVED);
		po = poRepo.saveAndFlush(po);
		return po;
	}

	public PurchaseOrder closePurchaseOrder(Long id) {
		PurchaseOrder po = poRepo.findOne(id);
		po.setStatus(Status.CLOSED);
		poRepo.save(po);
		return po;
	}

	public PurchaseOrder confirmInvoiceSent(Long id) {
		PurchaseOrder po = poRepo.findOne(id);
		po.setStatus(Status.INVOICED);
		poRepo.save(po);
		return po;
	}

	public PurchaseOrder createPurchaseOrder(PurchaseOrderResource poResource)
			throws ConferenceNotAvailableException {
		Conference conference = conferenceRepo.finAvailablebyIdAndbetweenDates(
				poResource.getConference().getId(), poResource.getStartDate(),
				poResource.getEndDate());
		if (conference == null)
			throw new ConferenceNotAvailableException(
					poResource.getConference().getId());

		PurchaseOrder po = new PurchaseOrder();
		po.setConference(conference);
		po.setStartDate(po.getStartDate());
		po.setEndDate(po.getEndDate());
		BigDecimal difference = new BigDecimal(
				getDateDifference(po.getStartDate(), po.getEndDate()));
		BigDecimal cost = conference.getPrice().multiply(difference);
		po.setCost(cost);
		po.setStatus(Status.PENDING);
		po = poRepo.saveAndFlush(po);
		return po;
	}

	public PurchaseOrder deliverConference(Long id) {
		PurchaseOrder po = poRepo.findOne(id);
		po.setStatus(Status.DELIVERED);
		poRepo.save(po);
		return po;
	}

	public PurchaseOrder dispatchConference(Long id) {
		PurchaseOrder po = poRepo.findOne(id);
		po.setStatus(Status.DISPATCHED);
		poRepo.save(po);
		return po;
	}

	public List<PurchaseOrder> getAll() {
		return poRepo.findAll();
	}

	public PurchaseOrder getPurchaseOrder(Long id) throws PONotFoundException {
		PurchaseOrder po = poRepo.findOne(id);
		if (po == null)
			throw new PONotFoundException(id);
		return po;
	}

	public PurchaseOrder rejectConference(Long id) {
		PurchaseOrder po = poRepo.findOne(id);
		po.setStatus(Status.REJECTED);
		poRepo.save(po);
		return po;
	}

	public PurchaseOrder returnConference(Long id) {
		PurchaseOrder po = poRepo.findOne(id);
		po.setStatus(Status.RETURNED);
		poRepo.saveAndFlush(po);
		return po;
	}

	public String requestExtension(Long id, String collection) throws ConferenceNotAvailableException{
		ObjectMapper mapper = new ObjectMapper();
		Map<String, Property> properties;
		PurchaseOrder po = poRepo.findOne(id);
		Conference conference = conferenceRepo.finAvailablebyIdAndbetweenDates(
				po.getConference().getId(), po.getStartDate(), po.getEndDate());
		if (conference == null) {
				throw new ConferenceNotAvailableException(
						po.getConference().getId());
		} else {
			try {
				properties = CollectionParserHelper.getProperties(collection);
				po.setEndDate(CollectionParserHelper.getLocalDate(properties,
						"endDate"));
				po.setStartDate(CollectionParserHelper.getLocalDate(properties,
						"startDate"));
				po.setStatus(Status.CLOSED);
				poRepo.saveAndFlush(po);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		PurchaseOrder newPo = new PurchaseOrder();
		newPo.setConference(conference);
		newPo.setStartDate(po.getStartDate());
		newPo.setEndDate(po.getEndDate());
		BigDecimal difference = new BigDecimal(
				getDateDifference(po.getStartDate(), po.getEndDate()));
		BigDecimal cost = conference.getPrice().multiply(difference);
		po.setCost(cost);
		newPo.setStatus(Status.PENDING_UPDATE);
		newPo = poRepo.saveAndFlush(newPo);
		return purchaseOrderResourceAssembler.toResourceString(newPo);
	}

	public PurchaseOrder save(PurchaseOrder po) {
		poRepo.saveAndFlush(po);
		return po;
	}

	public PurchaseOrder findOne(Long id) {
		return poRepo.findOne(id);
	}

	@Override
	public String createEntity(String collection) {
		PurchaseOrder po = new PurchaseOrder();
		try {
			po = purchaseOrderResourceAssembler.toModel(po, collection);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// Conference conference =
		// conferenceRepo.finAvailablebyIdAndbetweenDates(
		// po.getConference().getId(), po.getStartDate(), po.getEndDate());
		// if (conference == null)
		// throw new
		// ConferenceNotAvailableException(po.getConference().getId());

		// PurchaseOrder po = new PurchaseOrder();
		// po.setConference(conference);

		poRepo.save(po);
		return purchaseOrderResourceAssembler.toResourceString(po);
	}

	@Override
	public void deleteEntity(Long id) {
		poRepo.delete(id);
	}

	@Override
	public String updateEntity(Long id, String collection) {
		PurchaseOrder po = poRepo.findOne(id);
		try {
			po = purchaseOrderResourceAssembler.toModel(po, collection);
		} catch (Exception e) {
			e.printStackTrace();
		}
		poRepo.save(po);
		return purchaseOrderResourceAssembler.toResourceString(po);
	}

	@Override
	public String getSingleEntity(Long id) {
		return purchaseOrderResourceAssembler
				.toResourceString(poRepo.findOne(id));
	}

	@Override
	public String getAllEntities() {
		return purchaseOrderResourceAssembler
				.toResourcesString(poRepo.findAll());
	}
}
