package org.bench4q.agent.api;

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

import org.apache.log4j.Logger;
import org.bench4q.agent.datacollector.impl.BehaviorStatusCodeResult;
import org.bench4q.agent.plugin.ParameterFileCollector;
import org.bench4q.agent.scenario.Scenario;
import org.bench4q.agent.scenario.behavior.Behavior;
import org.bench4q.agent.scenario.engine.ScenarioContext;
import org.bench4q.agent.scenario.engine.ScenarioEngine;
import org.bench4q.agent.scenario.engine.Schedule;
import org.bench4q.share.helper.MarshalHelper;
import org.bench4q.share.models.agent.BehaviorBriefModel;
import org.bench4q.share.models.agent.BehaviorStatusCodeResultModel;
import org.bench4q.share.models.agent.CleanTestResultModel;
import org.bench4q.share.models.agent.RunScenarioModel;
import org.bench4q.share.models.agent.RunScenarioResultModel;
import org.bench4q.share.models.agent.StopTestModel;
import org.bench4q.share.models.agent.TestBriefStatusModel;
import org.bench4q.share.models.agent.scriptrecord.ScheduleModel;
import org.bench4q.share.models.agent.statistics.AgentBriefStatusModel;
import org.bench4q.share.models.agent.statistics.AgentBehaviorsBriefModel;
import org.bench4q.share.models.agent.statistics.AgentPageBriefModel;
import org.bench4q.share.models.agent.statistics.AgentPagesBriefModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

@Controller
@RequestMapping("/test")
public class TestController {
	private ScenarioEngine scenarioEngine;
	private ParameterFileCollector paramFileCollector;
	private Logger logger = Logger.getLogger(TestController.class);

	private ScenarioEngine getScenarioEngine() {
		return scenarioEngine;
	}

	@Autowired
	private void setScenarioEngine(ScenarioEngine scenarioEngine) {
		this.scenarioEngine = scenarioEngine;
	}

	private ParameterFileCollector getParamFileCollector() {
		return paramFileCollector;
	}

	@Autowired
	private void setParamFileCollector(ParameterFileCollector paramFileCollector) {
		this.paramFileCollector = paramFileCollector;
	}

	@RequestMapping(value = "/bookTest/{poolSize}", method = {
			RequestMethod.GET, RequestMethod.POST })
	@ResponseBody
	public RunScenarioResultModel bookTest(@PathVariable int poolSize) {
		try {
			UUID runId = UUID.randomUUID();
			this.getScenarioEngine().addRunningTestWithoutScenario(runId,
					poolSize);
			RunScenarioResultModel runScenarioResultModel = new RunScenarioResultModel();
			runScenarioResultModel.setRunId(runId);
			return runScenarioResultModel;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	@RequestMapping(value = "/submitScenarioWithParams/{runId}/{realStartTime}", method = RequestMethod.POST)
	@ResponseBody
	public String submitParams(
			@PathVariable UUID runId,
			@PathVariable long realStartTime,
			@RequestParam(value = "files[]", required = false) List<MultipartFile> files,
			@RequestParam(value = "scenarioModel", required = false) String scenarioModel,
			@RequestParam(value = "scheduleContent") String scheduleContent) {
		try {
			this.getParamFileCollector().collectParamFiles(files, runId);
			System.out.println(scenarioModel);
			RunScenarioModel runScenarioModel = (RunScenarioModel) MarshalHelper
					.unmarshal(RunScenarioModel.class, scenarioModel);
			System.out.println(scheduleContent);
			ScheduleModel scheduleModel = (ScheduleModel) MarshalHelper
					.unmarshal(ScheduleModel.class, scheduleContent);
			this.getScenarioEngine().submitScenario(runId,
					Scenario.scenarioBuilderWithCompile(runScenarioModel),
					Schedule.build(scheduleModel), realStartTime);
			return MarshalHelper.tryMarshal(buildWith(runId));
		} catch (Exception e) {
			logger.error("/submitScenarioWithParams", e);
			return null;
		}
	}

	private RunScenarioResultModel buildWith(UUID runId) {
		RunScenarioResultModel result = new RunScenarioResultModel();
		result.setRunId(runId);
		return result;
	}

	@RequestMapping(value = "/runWithParams/{runId}", method = RequestMethod.POST)
	@ResponseBody
	public RunScenarioResultModel runWithParams(@PathVariable UUID runId) {
		return this.getScenarioEngine().runWith(runId) ? buildWith(runId)
				: null;
	}

	@RequestMapping(value = "/briefAll/{runId}", method = RequestMethod.GET)
	@ResponseBody
	public TestBriefStatusModel briefAll(@PathVariable UUID runId) {
		TestBriefStatusModel result = new TestBriefStatusModel();
		result.setScenarioBriefModel(this.brief(runId));
		result.setPagesBriefModel(this.pagesBrief(runId));
		result.setBehaviorsBriefModel(this.behaviorsBrief(runId));
		return result;
	}

	@RequestMapping(value = "/brief/{runId}/{behaviorId}", method = RequestMethod.GET)
	@ResponseBody
	public BehaviorBriefModel behaviorBrief(@PathVariable UUID runId,
			@PathVariable int behaviorId) {
		ScenarioContext scenarioContext = this.getScenarioEngine()
				.getRunningTests().get(runId);
		if (scenarioContext == null) {
			return null;
		}
		Map<Integer, BehaviorStatusCodeResult> map = scenarioContext
				.getDataStatistics().getBehaviorBriefStatistics(behaviorId);
		return buildBehaviorBrief(runId, behaviorId, "", map);
	}

	private BehaviorBriefModel buildBehaviorBrief(UUID runId, int behaviorId,
			String behaviorUrl, Map<Integer, BehaviorStatusCodeResult> map) {
		List<BehaviorStatusCodeResultModel> detailStatusCodeResultModels = new ArrayList<BehaviorStatusCodeResultModel>();
		for (int statusCode : map.keySet()) {
			BehaviorStatusCodeResultModel behaviorStatusCodeResultModel = new BehaviorStatusCodeResultModel();
			BehaviorStatusCodeResult detailStatusCodeResult = map
					.get(statusCode);
			behaviorStatusCodeResultModel.setStatusCode(statusCode);
			behaviorStatusCodeResultModel
					.setCount(detailStatusCodeResult.count);
			behaviorStatusCodeResultModel
					.setContentLength(detailStatusCodeResult.contentLength);
			behaviorStatusCodeResultModel
					.setMinResponseTime(detailStatusCodeResult.minResponseTime);
			behaviorStatusCodeResultModel
					.setMaxResponseTime(detailStatusCodeResult.maxResponseTime);
			behaviorStatusCodeResultModel
					.setContentType(detailStatusCodeResult.contentType);
			behaviorStatusCodeResultModel
					.setTotalResponseTimeThisTime(detailStatusCodeResult.totalResponseTimeThisTime);
			detailStatusCodeResultModels.add(behaviorStatusCodeResultModel);
		}
		BehaviorBriefModel behaviorBriefModel = new BehaviorBriefModel();
		behaviorBriefModel.setBehaviorId(behaviorId);
		behaviorBriefModel
				.setDetailStatusCodeResultModels(detailStatusCodeResultModels);
		behaviorBriefModel.setBehaviorUrl(behaviorUrl);
		return behaviorBriefModel;
	}

	@RequestMapping(value = "/pagesBrief/{runId}")
	@ResponseBody
	public AgentPagesBriefModel pagesBrief(@PathVariable UUID runId) {
		ScenarioContext context = this.getScenarioEngine().getRunningTests()
				.get(runId);
		AgentPagesBriefModel result = new AgentPagesBriefModel();
		List<AgentPageBriefModel> pageBrieves = new ArrayList<AgentPageBriefModel>();

		if (context == null || context.isFinished()) {
			return null;
		}
		for (int i = 0; i < context.getScenario().getPages().length; i++) {
			pageBrieves.add((AgentPageBriefModel) context.getDataStatistics()
					.getPageBriefStatistics(i));
		}
		result.setPageBriefModels(pageBrieves);
		return result;
	}

	@RequestMapping(value = "/pageBrief/{runId}/{pageId}")
	@ResponseBody
	public AgentPageBriefModel pageBrief(@PathVariable UUID runId,
			@PathVariable int pageId) {
		ScenarioContext context = this.getScenarioEngine().getRunningTests()
				.get(runId);
		if (context == null || context.isFinished()) {
			return null;
		}
		return (AgentPageBriefModel) context.getDataStatistics()
				.getPageBriefStatistics(pageId);
	}

	@RequestMapping(value = "/behaviorsBrief/{runId}")
	@ResponseBody
	public AgentBehaviorsBriefModel behaviorsBrief(@PathVariable UUID runId) {
		AgentBehaviorsBriefModel ret = new AgentBehaviorsBriefModel();
		List<BehaviorBriefModel> behaviorBriefModels = new ArrayList<BehaviorBriefModel>();
		ScenarioContext scenarioContext = this.getScenarioEngine()
				.getRunningTests().get(runId);
		if (scenarioContext == null || scenarioContext.isFinished()) {
			return null;
		}
		for (Behavior behavior : scenarioContext.getScenario()
				.getAllBehaviors()) {
			int behaviorId = behavior.getId();
			Map<Integer, BehaviorStatusCodeResult> map = behavior
					.getBehaviorBriefResult(scenarioContext.getDataStatistics());
			if (map == null) {
				continue;
			}
			behaviorBriefModels.add(buildBehaviorBrief(runId, behaviorId,
					behavior.getSpecificParamValue("url"), map));
		}
		ret.setBehaviorBriefModels(behaviorBriefModels);
		return ret;
	}

	@RequestMapping(value = "/brief/{runId}", method = RequestMethod.GET)
	@ResponseBody
	public AgentBriefStatusModel brief(@PathVariable UUID runId) {
		ScenarioContext scenarioContext = this.getScenarioEngine()
				.getRunningTests().get(runId);
		if (scenarioContext == null) {
			return null;
		}
		AgentBriefStatusModel agentStatusModel = (AgentBriefStatusModel) scenarioContext
				.getDataStatistics().getScenarioBriefStatistics();
		agentStatusModel.setvUserCount(scenarioContext.getExecutor()
				.getActiveCount());
		return agentStatusModel;
	}

	@RequestMapping(value = "/stop/{runId}", method = { RequestMethod.GET,
			RequestMethod.POST })
	@ResponseBody
	public StopTestModel stop(@PathVariable UUID runId) {
		System.out.println("stop method");
		ScenarioContext scenarioContext = this.getScenarioEngine()
				.getRunningTests().get(runId);
		if (scenarioContext == null) {
			return null;
		}
		System.out.println("when before stop, classId:"
				+ scenarioContext.getExecutor().toString());
		System.out.println("when after stop, classId:"
				+ scenarioContext.getExecutor().toString());
		scenarioContext.stop();
		clean();
		return new StopTestModel(true);
	}

	@RequestMapping(value = "/clean", method = RequestMethod.GET)
	@ResponseBody
	public CleanTestResultModel clean() {
		System.gc();
		return new CleanTestResultModel(true);
	}

}
