package org.bench4q.master.unitTest.service;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.UUID;

import org.apache.commons.io.FileUtils;
import org.bench4q.master.Main;
import org.bench4q.master.domain.entity.Agent;
import org.bench4q.master.domain.entity.Script;
import org.bench4q.master.domain.entity.TestPlan;
import org.bench4q.master.domain.entity.TestPlanScript;
import org.bench4q.master.domain.entity.User;
import org.bench4q.master.domain.repository.ScriptRepositoty;
import org.bench4q.master.domain.service.AgentService;
import org.bench4q.master.unitTest.TestBase_MakeUpTestPlan;
import org.bench4q.share.enums.master.TestPlanStatus;
import org.bench4q.share.helper.TestHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.multipart.MultipartFile;

import static org.junit.Assert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:mockHttpMesseger-context.xml" })
@FixMethodOrder(MethodSorters.JVM)
public class Test_TestPlanEngine extends TestBase_MakeUpTestPlan {
	@Autowired
	private AgentService agentService;
	@Autowired
	private ScriptRepositoty scriptRepository;

	@Before
	public void prepare() throws IOException {
		synchronized (Test_TestPlanEngine.class) {
			this.scriptRepository.attach(Script.createScriptWithoutId("test1",
					FileUtils.readFileToString(new File(
							"Scripts/forGoodRecord.xml")), this
							.getUserRepository().getUser("admin"), Collections
							.<MultipartFile> emptyList()));
			this.getAgentRepository().detach("147.0.0.1");
			this.agentService.addAgentToPool(Agent.createAgentWithoutId(
					"147.0.0.1", 6565, 500));
			prepareForTestPlanRunning();
			submitATestPlanWithOneScript();
		}
	}

	@After
	public void cleanUp() {
		synchronized (Test_TestPlanEngine.class) {
			cleanUpForTestPlanRunning();
			Agent agent = null;
			if ((agent = this.getAgentRepository().getAgentBy("147.0.01")) != null) {
				this.getAgentRepository().detach(agent.getId());
			}
			Script script = this.scriptRepository.getScriptByName("test1");
			if (script == null) {
				System.out.println("script is null");
			} else {
				this.scriptRepository.detach(script.getId());
			}
		}
	}

	@Test
	public void testCommintComplete() throws Exception {
		TestPlanStatus status = TestPlanStatus.Complete;
		testForStatus(status);
	}

	@Test
	public void testRunTestPlanRightly() throws InterruptedException {
		assertNotNull(this.getAgentMessenger()
				.getStatus(
						Agent.createAgentWithoutId(Test_AGENT_HOSTNAME,
								TEST_PORT, 500)));
		User user = this.getUserRepository().getUser("admin");
		int scriptId = getUserFirstScript(user);
		this.getHaPool().checkAllHeartBeat();
		Date dateBeforeRun = new Date();
		Thread.sleep(1000);
		this.setTestPlanRunIdUuid(this.getTestPlanEngine().runWith(
				createATestPlanWithOneScript(scriptId), user));
		assertNotNull(getTestPlanRunIdUuid());
		Thread.sleep(10000);
		TestPlan testPlan = this.getTestPlanRepository().getTestPlanInDomainBy(
				getTestPlanRunIdUuid());
		assertEquals(TestPlanStatus.InRunning,
				TestPlanStatus.valueOf(testPlan.getCurrentStatus()));
		assertTrue(testPlan.getLastRunningTime().after(dateBeforeRun));
		assertEquals(TestPlanStatus.InRunning,
				TestPlanStatus.valueOf(getTestPlanRepository()
						.getRunningTestPlanBy(getTestPlanRunIdUuid())
						.getCurrentStatus()));
		assertNotNull(this.getTestPlanRepository().getRunningTestPlanBy(
				getTestPlanRunIdUuid()));
		for (TestPlanScript testPlanScript : testPlan.getTestPlanScripts()) {
			testPlanScript.stop();
		}
		this.getTestPlanEngine().doTaskComplete(getTestPlanRunIdUuid());
	}

	@Test
	public void testRunWithWronglyWhenNoEnoughLoadInPool()
			throws InterruptedException {
		System.out.println("This need each agent not in running");
		this.submitATestPlanWithOneScript();
		this.getHaPool().getPool().clear();
		Thread.sleep(10000);
		TestPlan testPlan = this.getTestPlanRepository().getTestPlanInDomainBy(
				getTestPlanRunIdUuid());
		assertNotEquals(TestPlanStatus.InRunning,
				TestPlanStatus.valueOf(testPlan.getCurrentStatus()));
		assertNull(this.getTestPlanRepository().getRunningTestPlanBy(
				getTestPlanRunIdUuid()));
		deleteTestPlan();
	}

	@Test
	public void testPickATestPlan() {
		this.getHaPool().checkAllHeartBeat();
		Main.getPortToServe();
		assertNotEquals(0, this.getHaPool().getCurrentAvailableLoad());
		System.out.println(this.getHaPool().getCurrentAvailableLoad());
		TestPlan testPlanPicked = this.getTestPlanEngine().pickATestPlan();
		assertNotNull(testPlanPicked);
		assertTrue(testPlanPicked.getRequiredLoad() <= this.getHaPool()
				.getCurrentAvailableLoad());
		assertTrue(testPlanPicked.getFailTimes() <= Main.MAX_FAIL_TIMES);
		assertTrue(System.currentTimeMillis()
				- testPlanPicked.getLastRunningTime().getTime() > Main.MIN_EXECUTE_INTERVAL_IN_SECONDS * 1000);
		assertNotEquals(TestPlanStatus.Complete,
				TestPlanStatus.valueOf(testPlanPicked.getCurrentStatus()));
	}

	// @Test
	// public void testPickAndRunByCycle() throws InterruptedException {
	// this.getHaPool().checkAllHeartBeat();
	// assertEquals(500, this.getHaPool().getCurrentAvailableLoad());
	// Main.getPortToServe();
	// UUID testPlanRunId = this.submitATestPlanWithOneScript();
	// Thread.sleep(71000);
	// assertEquals(
	// TestPlanStatus.InRunning,
	// TestPlanStatus.valueOf(this.getTestPlanRepository()
	// .getTestPlanInDomainBy(testPlanRunId)
	// .getCurrentStatus()));
	// deleteTestPlan();
	// }

	private void testForStatus(TestPlanStatus status) throws Exception {
		this.submitATestPlanWithOneScript();
		TestPlan testPlan = this.getTestPlanRepository().getTestPlanInDomainBy(
				getTestPlanRunIdUuid());
		testPlan.run();
		this.getTestPlanRepository().attachRunningTestPlan(testPlan);
		TestHelper.invokePrivate(this.getTestPlanEngine(),
				"commit" + status.name(), new Class[] { TestPlan.class },
				new Object[] { this.getTestPlanRepository()
						.getRunningTestPlanBy(getTestPlanRunIdUuid()) });
		assertEquals(status,
				TestPlanStatus.valueOf(testPlan.getCurrentStatus()));
		assertEquals(
				status,
				TestPlanStatus.valueOf(this.getTestPlanRepository()
						.getRunningTestPlanBy(getTestPlanRunIdUuid())
						.getCurrentStatus()));
		deleteTestPlan();
	}

	@Test
	public void test_Stop() {
		int loadBefore = this.getHaPool().getCurrentAvailableLoad();
		this.getTestPlanEngine().doRunTestPlan(this.getTestPlanRunIdUuid());
		int afterLoad = this.getHaPool().getCurrentAvailableLoad();
		assertEquals(0, afterLoad);
		this.getTestPlanEngine().stop(this.getTestPlanRunIdUuid());
		int lastLoad = this.getHaPool().getCurrentAvailableLoad();
		assertEquals(loadBefore, lastLoad);
	}

	@Test
	public void test_stopWrongTestPlanId() {
		assertFalse(this.getTestPlanEngine().stop(UUID.randomUUID()));
	}
}
