package org.bench4q.master.unitTest.entity;

import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.bench4q.master.domain.RunningAgentInterface;
import org.bench4q.master.domain.entity.Agent;
import org.bench4q.master.domain.entity.RunningAgentDB;
import org.bench4q.master.domain.entity.TestPlan;
import org.bench4q.master.domain.entity.TestPlanScript;
import org.bench4q.master.domain.factory.RunningAgentFactory;
import org.bench4q.master.domain.valueobject.transaction.TransactionFactory;
import org.bench4q.master.domain.valueobject.transaction.impl.ScriptLoadApplication;
import org.bench4q.master.unitTest.TestBase_MakeUpTestPlan;
import org.bench4q.share.enums.master.TestPlanStatus;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:mockHttpMesseger-context.xml" })
public class Test_RunningAgent extends TestBase_MakeUpTestPlan {
	@Autowired
	private RunningAgentFactory runningAgentFactory;

	@Autowired
	private TransactionFactory transactionFactory;

	private List<Agent> testcase2 = new ArrayList<Agent>();
	{
		testcase2.add(Agent.createAgentWithoutId("127.0.0.1", 6565, 500));
		testcase2.add(Agent.createAgentWithoutId("127.0.0.2", 6565, 500));
	}

	@Before
	public void before() {
		for (Agent agent : this.testcase2) {
			this.getAgentService().addAgentToPool(agent);
		}
		this.getHaPool().checkAllHeartBeat();
		this.submitATestPlanWithOneScript();
	}

	@Test
	public void test_distributeScriptAndParams() {
		TestPlan testPlan = this.getTestPlanRepository().getTestPlanInDomainBy(
				getTestPlanRunIdUuid());
		testPlan.run();
		assertEquals(TestPlanStatus.InRunning.name(),
				testPlan.getCurrentStatus());
		TestPlanScript runningScript = testPlan
				.extracSpecifiedScript(getUserFirstScript(this
						.getUserRepository().getUser("admin")));
		// TODO: fulfill this
		RunningAgentDB runningAgentDB = getFirstRunningAgentDB(runningScript);
		assertNotNull(runningAgentDB.getAgentMessenger());
		assertTrue(runningAgentDB.distributeScriptAndParams());
		assertNotNull(runningAgentDB.distributeScriptAndParams());
		runningScript.stop();
		this.getTestPlanEngine().doTaskComplete(getTestPlanRunIdUuid());
	}

	private RunningAgentDB getFirstRunningAgentDB(TestPlanScript testPlanScript) {
		for (RunningAgentDB runningAgentDB : testPlanScript
				.getRunningAgentsDB()) {
			return runningAgentDB;
		}
		return null;
	}

	@Test
	public void test_stop() {
		synchronized (this.getHaPool()) {
			TestPlanScript runningScript = this.getTestPlanRepository()
					.getTestPlanInDomainBy(getTestPlanRunIdUuid())
					.extracSpecifiedScript(getScriptId());
			int currentLoadBeforeRun = this.getHaPool()
					.getCurrentAvailableLoad();
			ScriptLoadApplication application = (ScriptLoadApplication) this.transactionFactory
					.buildScriptTransaction(runningScript,
							EACH_SCRIPT_LOAD_SMALLSCALE);
			RunningAgentDB underTest = null;

			underTest = (RunningAgentDB) application.execute().get(0);
			assertTrue(underTest.distributeScriptAndParams());

			assertTrue(underTest.run());
			int currentLoadAfterRun = this.getHaPool()
					.getCurrentAvailableLoad();
			assertEquals(currentLoadBeforeRun - 500, currentLoadAfterRun);
			assertTrue(underTest.stop());
			int currentLoadAfterStop = this.getHaPool()
					.getCurrentAvailableLoad();
			assertEquals(currentLoadBeforeRun, currentLoadAfterStop);
		}

	}

	@Test
	public void test_stopTwiceInPractice() {
		synchronized (this.getHaPool()) {
			TestPlanScript runningScript = this.getTestPlanRepository()
					.getTestPlanInDomainBy(getTestPlanRunIdUuid())
					.extracSpecifiedScript(getScriptId());
			int currentLoadBeforeRun = this.getHaPool()
					.getCurrentAvailableLoad();
			assertEquals(1000, currentLoadBeforeRun);
			this.getTestPlanEngine().doRunTestPlan(this.getTestPlanRunIdUuid());
			RunningAgentInterface runningAgent = (RunningAgentInterface) refetchRunningScript()
					.getRunningAgents().toArray()[0];
			String hostName = runningAgent.getAgent().getHostName();
			assertTrue(runningAgent.stop());
			// refetch runningScript
			runningScript = refetchRunningScript();
			for (RunningAgentInterface runningAgentInterface : runningScript
					.getRunningAgents()) {
				if (runningAgentInterface.getAgent().getHostName()
						.equals(hostName)) {
					assertTrue(runningAgentInterface.stop());
				}
			}
		}
	}

	private TestPlanScript refetchRunningScript() {
		TestPlan runningTestPlanBy = this.getTestPlanRepository()
				.getRunningTestPlanBy(getTestPlanRunIdUuid());
		return runningTestPlanBy.extracSpecifiedScript(getScriptId());
	}

	@Test
	public void test_stopTwice() {
		RunningAgentDB runningAgentDB = RunningAgentDB
				.buildRunningAgentDBWithoutId(this.testcase2.get(0), 10, null,
						UUID.randomUUID());
		this.runningAgentFactory.convertToDomain(runningAgentDB);
		assertTrue(runningAgentDB.run());
		assertTrue(runningAgentDB.stop());
		assertTrue(runningAgentDB.stop());
	}
}
