package info.bukova.isspst.services.requirement;

import info.bukova.isspst.Constants;
import info.bukova.isspst.data.Invoicing;
import info.bukova.isspst.data.Limit;
import info.bukova.isspst.data.Requirement;
import info.bukova.isspst.data.RequirementItem;
import info.bukova.isspst.data.RequirementState;
import info.bukova.isspst.data.RequirementSubject;
import info.bukova.isspst.data.User;
import info.bukova.isspst.data.Workflow;
import info.bukova.isspst.services.LazyLoader;
import info.bukova.isspst.services.invoicing.InvoicingService;
import info.bukova.isspst.services.limits.LimitService;
import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * @author Pepa Rokos
 * @author Franta Přibyl
 * 
 * Bázová třída požadavků na materiál a služby.
 *
 */
public class RequirementServiceImpl extends RequirementBaseServiceImpl<Requirement> implements RequirementService, RequirementBaseService<Requirement>
{

	@Autowired
	private RequirementTypeService reqTypeService;
	@Autowired
	private InvoicingService invoicingService;
	@Autowired
	private LimitService limitService;

	@Override
	protected Requirement createEntity()
	{
		Requirement entity = new Requirement();

		entity.setReqDate(new Date());
		entity.setType(reqTypeService.getTypeById(Constants.REQTYPE_ORDER));
		entity.setState(RequirementState.NEW);

		return entity;
	}

	@Override
	protected boolean autoApprove(Requirement entity, Date approveDate)
	{
		List<User> approvers = this.getNextApprover(entity);
		Workflow nextWf = this.getNextWorkflow(entity);

		if (approvers == null || approvers.isEmpty() || nextWf == null || nextWf.getLimit() == null)
		{
			return false;
		}

		if ((entity.getSumTotal() != null) 
				&& (entity.getSumTotal().compareTo(nextWf.getLimit()) == -1))
		{
			approve(entity, approvers.get(0), approveDate, null);
			return true;
		}

		return false;
	}

	/**
	 * Nadlimitní autorizovat bez podpisu
	 * @param wf Další krok schválení
	 * @return true, pokud má role nastavený limit- automatické schválení podlimitního požadavku
	 */
	@Override
	protected boolean signatureNotRequired(Workflow wf) {
		return wf.getLimit() != null;
	}

	@Override
	protected boolean canAdd(Requirement entity)
	{
		if (entity.getWorkgroup() != null) {
			Limit limit = limitService.getForWorkgroup(entity.getWorkgroup());
			if (limit == null) {
				return true;
			}

			BigDecimal total = invoicingService.totalInvoicedForWorkgroup(entity.getWorkgroup());

			if (total == null) {
				total = BigDecimal.ZERO;
			}

			total = total.add(entity.getSumTotal());

			if (total.compareTo(limit.getLimit()) <= 0)	{
				return true;
			}
		}
		else {
			return true;
		}

		throw new AddRequirementException("LimitExceeded");
	}

	/*
	 * Lazy load pro načtení seznamu skupin materiálu nebo služeb do comba.
	 * Ošetřuje se zde případné neexistující vazby na ID smazaných skupin
	 * materiálu nebo služeb.
	 * 
	 * @see
	 * info.bukova.isspst.services.requirement.RequirementService#loadGroups
	 * (info.bukova.isspst.data.Requirement)
	 */
	@Transactional
	@LazyLoader("form")
	@Override
	public void loadGroups(Requirement req)
	{
		if (req == null)
		{
			return;
		}

		Requirement reqDb = this.getById(req.getId());

		// Načíst položky požadavku
		List<RequirementItem> items = reqDb.getItems();

		for (int i = 0; i < items.size(); i++)
		{
			RequirementItem item = items.get(i);

			try
			{
				// K položce získat skupinu materiálnu nebo služby
				RequirementSubject subject = item.getReqSubject();
				// Pokud ID skupiny nebylo nalezeno, padá do výjimky
				// Pokud ID skupiny bylo nalezeno, přinutí zavolání metody
				// načíst záznam se skupinou = lazy load
				subject.getCode();
				// Zapsat načtená data o skupině
				item.setReqSubject(subject);
			}
			catch (Throwable e)
			{
				item.setReqSubject(null);
			}
		}

		// Zaktualizovat položky záznamu tak, aby byly odkazy na skupiny
		// materiálů a služeb konzistentní
		req.setItems(items);
	}
	
	@Override
	@Transactional
	@LazyLoader("form")
	public void loadItems(Requirement req) {
		if (req == null) {
			return;
		}
		
		Requirement r = getById(req.getId());
		Hibernate.initialize(r.getItems());
		req.setItems(r.getItems());
	}

	@Override
	public BigDecimal calcTotalFromItem(RequirementItem item)
	{
		BigDecimal total = BigDecimal.ZERO;

		if (item != null)
		{
			total = item.getQuantity().multiply(item.getUnitPrice());
		}

		return total;
	}

	@Override
	public RequirementItem calcTotalInItem(RequirementItem item)
	{
		RequirementItem newItem = item;

		if (newItem != null)
		{
			BigDecimal total = this.calcTotalFromItem(newItem);
			newItem.setTotal(total);
		}

		return newItem;
	}

	@Override
	public RequirementItem calcItemValuesFromItemTotal(RequirementItem item)
	{
		RequirementItem newItem = item;

		if (newItem != null)
		{
			BigDecimal quantity = newItem.getQuantity();

			if (quantity.equals(BigDecimal.ZERO))
			{
				newItem.setUnitPrice(BigDecimal.ZERO);
			}
			else
			{
				newItem.setUnitPrice(newItem.getTotal().divide(quantity, 2, BigDecimal.ROUND_HALF_UP));
			}
		}

		return newItem;
	}

	@Override
	public BigDecimal calcSumTotalFromItems(List<RequirementItem> items)
	{
		BigDecimal sumTotal = BigDecimal.ZERO;

		if (items != null)
		{
			for (RequirementItem item : items)
			{
				if (item != null)
				{
					BigDecimal total = item.getTotal();

					if (total != null)
					{
						sumTotal = sumTotal.add(total);
					}
				}
			}
		}

		return sumTotal;
	}

	@Override
	protected void postApprove(Requirement entity) {
		if (entity.getState() == RequirementState.APPROVED) {
			Invoicing inv = new Invoicing();
			inv.setRequirement(entity);
			invoicingService.add(inv);
		}
	}

	@Override
	@Transactional
	public BigDecimal getInvoicedAmount(Requirement req) {
		Invoicing inv = invoicingService.getForRequirement(req);
		return inv != null ? inv.getTotalInvoiced() : null;
	}

	@Override
	@Transactional
	@PreAuthorize("hasPermission(this, 'PERM_EDIT') or hasPermission(#entity, this.getUpdateEntityPermission())")
	public void update(Requirement entity) {
		super.update(entity);
		Invoicing inv = invoicingService.getForRequirement(entity);

		if (inv != null) {
			invoicingService.delete(inv);
		}
	}
}
