package de.hsadmin.core.model;

import java.util.LinkedList;
import java.util.List;

import javax.persistence.EntityManager;

import de.hsadmin.mods.user.UnixUser;

/**
 *  allows access only for hostmasters, used as fallback wrapper.
 */
public class SecureDefaultModuleImpl extends AbstractModuleImpl {
	
	@Override
	public AbstractEntity initialize(AbstractEntity newEntity) throws AuthorisationException {
		return super.initialize(newEntity);
	}

	@Override
	public AbstractEntity find(Class<? extends AbstractEntity> entityClass, Object key) throws HSAdminException {
		Transaction transaction = getTransaction();
		AbstractEntity entity = super.find(entityClass, key);
		if (entity != null && !entity.isReadAllowedFor(transaction.getLoginUser()))
			throw new AuthorisationException(transaction.getLoginUser(), "find");
		return entity;
	}

	@Override
	public AbstractEntity findByString(Class<? extends AbstractEntity> entityClass, String key) throws HSAdminException {
		Transaction transaction = getTransaction();
		AbstractEntity entity = super.findByString(entityClass, key);
		if (entity != null && !entity.isReadAllowedFor(transaction.getLoginUser()))
			throw new AuthorisationException(transaction.getLoginUser(), "findByString");
		return entity;
	}

	@Override
	public List<AbstractEntity> search(Class<? extends AbstractEntity> entityClass, String condition, String orderBy) 
			throws HSAdminException {
		Transaction transaction = getTransaction();
		// restrict query to entities where the loginUser could have rights on
		SearchFilter filterAnnot;
		if (!transaction.getLoginUser().hasHostmasterRole()
				&& (filterAnnot = getSecurityFilterAnnotation(entityClass)) != null) {
			String securityCondition = filterAnnot.value();
			if (condition != null && condition.length() > 0) {
				condition = "(" + condition + ")" + " AND ( " + securityCondition + ")";
			}
			else {
				condition = securityCondition;
			}
		}

		// do query
		List<AbstractEntity> res = super.search(entityClass, condition, orderBy);
		List<AbstractEntity> ret = new LinkedList<AbstractEntity>();

		// remove entities where login user has no access rights
		if (res != null) {
			for (AbstractEntity entity : res) {
				AbstractEntity returnedEntity = entity;
				if (returnedEntity.isReadAllowedFor(transaction.getLoginUser()))
					ret.add(returnedEntity);
			}
		}

		// return clean result
		return ret;

	}

	// helper method to get annotation from class or superclass
	private SearchFilter getSecurityFilterAnnotation(Class<?> entityClass) {
		SearchFilter ret;
		while (entityClass != null) {
			ret = (SearchFilter) entityClass.getAnnotation(SearchFilter.class);
			if (ret != null)
				return ret;
			entityClass = entityClass.getSuperclass();
		}
		return null;
	}

	@Override
	public AbstractEntity add(AbstractEntity newEntity) throws HSAdminException {
		// access rights checking is done by base class
		return super.add(newEntity);
	}

	@Override
	public AbstractEntity update(AbstractEntity existingEntity) throws HSAdminException {
		// access rights checking is done by base class
		return super.update(existingEntity);
	}

	@Override
	public void delete(AbstractEntity detachedEntity) throws HSAdminException {
		Transaction transaction = getTransaction();
		EntityManager entityManager = transaction.getEntityManager();
		UnixUser loginUser = transaction.getLoginUser();
		AbstractEntity attachedEntity = entityManager.find(detachedEntity.getClass(), detachedEntity.id());
		if (!attachedEntity.isWriteAllowedFor(loginUser)) {
			throw new AuthorisationException(loginUser, "delete", detachedEntity);
		}
		super.delete(attachedEntity);
	}

}
