package org.bench4q.master.domain.repository;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.log4j.Logger;
import org.bench4q.master.domain.entity.Monitor;
import org.bench4q.master.domain.entity.MonitorResult;
import org.bench4q.master.domain.entity.TestPlan;
import org.bench4q.master.domain.entity.TestPlanScript;
import org.bench4q.master.domain.entity.TestPlanScriptResult;
import org.bench4q.master.domain.entity.User;
import org.bench4q.master.domain.factory.TestPlanFactory;
import org.bench4q.master.exception.ExceptionLog;
import org.bench4q.master.exception.ExceptionUtils.EntityUniqueAlReadyExistException;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class TestPlanRepository extends AbstractRepositoty {
	private TestPlanFactory testPlanFactory;
	private final Map<UUID, TestPlan> runningTestPlans = new HashMap<UUID, TestPlan>();
	private Logger logger = Logger.getLogger(TestPlanRepository.class);

	private TestPlanFactory getTestPlanFactory() {
		return testPlanFactory;
	}

	@Autowired
	private void setTestPlanFactory(TestPlanFactory testPlanFactory) {
		this.testPlanFactory = testPlanFactory;
	}

	private Map<UUID, TestPlan> getRunningTestPlans() {
		return runningTestPlans;
	}

	public boolean attach(TestPlan testPlanDBWithoutId) {
		Session session = this.getSessionHelper().openSession();
		Transaction transaction = session.beginTransaction();
		try {
			session.merge(testPlanDBWithoutId);
			transaction.commit();
			return true;
		} catch (Exception e) {
			logger.error(ExceptionLog.getStackTrace(e));
			transaction.rollback();
			return false;
		} finally {
			releaseSession(session);
		}
	}

	@Override
	protected void guardOtherUniqueConditionForEntity(
			String uniquePropertyName, String value)
			throws EntityUniqueAlReadyExistException {
		if (getTestPlanInDomainBy(UUID.fromString(value)) != null) {
			throw new EntityUniqueAlReadyExistException(
					"Testplan with the testPlanRunId " + value
							+ "already exists");
		}
	}

	public boolean detach(int id) {
		Session session = this.getSessionHelper().openSession();
		Transaction transaction = session.beginTransaction();
		try {
			TestPlan testPlanInDB = (TestPlan) session
					.createCriteria(TestPlan.class)
					.add(Restrictions.eq("id", id)).uniqueResult();
			if (testPlanInDB == null) {
				return false;
			}
			session.delete(testPlanInDB);
			transaction.commit();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			transaction.rollback();
			return false;
		} finally {
			releaseSession(session);
		}
	}

	public TestPlan getTestPlanInDomainBy(UUID testPlanRunId) {
		TestPlan result = null;
		Session session = this.getSessionHelper().openSession();
		result = (TestPlan) session
				.createCriteria(TestPlan.class)
				.add(Restrictions.eq("testPlanRunId", testPlanRunId.toString()))
				.uniqueResult();
		if (result == null) {
			logger.error("can't find testplan with testPlanRunId : "
					+ testPlanRunId.toString() + " in TestPlanRepository");
		}
		releaseSession(session);
		return this.getTestPlanFactory().convertToDomain(result);
	}

	public TestPlan doGetTestPlanBy(Session session, UUID testPlanRunId) {
		return (TestPlan) session
				.createCriteria(TestPlan.class)
				.add(Restrictions.eq("testPlanRunId", testPlanRunId.toString()))
				.uniqueResult();
	}

	public List<TestPlan> loadEntities(User user) {
		Session session = this.getSessionHelper().openSession();
		try {
			@SuppressWarnings("unchecked")
			List<TestPlan> ret = session.createCriteria(TestPlan.class)
					.add(Restrictions.eq("user", user))
					.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
			return ret;
		} catch (Exception e) {
			logger.error(ExceptionLog.getStackTrace(e));
			return null;
		} finally {
			releaseSession(session);
		}
	}

	@SuppressWarnings("unchecked")
	public List<TestPlan> loadTestPlansBy(List<Criterion> criterions,
			Order order) {
		Session session = this.getSessionHelper().openSession();
		try {
			Criteria criteria = session.createCriteria(TestPlan.class);
			for (Criterion criterion : criterions) {
				criteria.add(criterion);
			}
			return criteria.addOrder(order)
					.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
		} catch (Exception e) {
			logger.error(ExceptionLog.getStackTrace(e));
			return new LinkedList<TestPlan>();
		} finally {
			releaseSession(session);
		}
	}

	public boolean updateEntity(TestPlan testPlanDB) {
		if (testPlanDB == null) {
			return false;
		}
		Session session = this.getSessionHelper().openSession();
		Transaction transaction = session.beginTransaction();
		try {
			session.update(testPlanDB);
			transaction.commit();
			return true;
		} catch (Exception e) {
			transaction.rollback();
			logger.error(ExceptionLog.getStackTrace(e));
			return false;
		} finally {
			releaseSession(session);
		}
	}

	public boolean doUpdateEntity(Session session, TestPlan testPlan) {
		Transaction transaction = session.beginTransaction();
		try {
			session.update(testPlan);
			transaction.commit();
			return true;
		} catch (Exception e) {
			transaction.rollback();
			logger.error(ExceptionLog.getStackTrace(e));
			return false;
		}
	}

	public TestPlan getRunningTestPlanBy(UUID testPlanRunId) {
		return this.getRunningTestPlans().get(testPlanRunId);
	}

	public void attachRunningTestPlan(TestPlan testPlan) {
		this.getRunningTestPlans().put(
				UUID.fromString(testPlan.getTestPlanRunId()), testPlan);
	}

	public void detachRunningTestPlan(UUID testPlanRunId) {
		this.getRunningTestPlans().remove(testPlanRunId);
	}

	@SuppressWarnings("unchecked")
	public List<MonitorResult> getMonitorResultsByMonitor(Monitor monitor) {
		Session session = this.getSessionHelper().openSession();
		try {

			return session.createCriteria(MonitorResult.class)
					.add(Restrictions.eq("monitor", monitor))
					.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();

		} catch (Exception e) {
			logger.error(ExceptionLog.getStackTrace(e));
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	public List<TestPlanScriptResult> getScriptResultsByTestPlanScript(
			TestPlanScript testPlanScript) {
		Session session = this.getSessionHelper().openSession();
		try {

			return session.createCriteria(TestPlanScriptResult.class)
					.add(Restrictions.eq("testPlanScript", testPlanScript))
					.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();

		} catch (Exception e) {
			logger.error(ExceptionLog.getStackTrace(e));
			return null;
		}
	}

	public Collection<TestPlan> loadRunningTestPlans() {
		return this.runningTestPlans.values();
	}
}
