package org.bench4q.web.api;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBException;

import org.apache.log4j.Logger;
import org.bench4q.share.helper.ExceptionLog;
import org.bench4q.share.helper.MarshalHelper;
import org.bench4q.share.models.agent.BehaviorStatusCodeResultModel;
import org.bench4q.share.models.master.MonitorModel;
import org.bench4q.share.models.master.ScriptHandleModel;
import org.bench4q.share.models.master.TestPlanDBModel;
import org.bench4q.share.models.master.TestPlanModel;
import org.bench4q.share.models.master.TestPlanResponseModel;
import org.bench4q.share.models.master.TestPlanResultModel;
import org.bench4q.share.models.master.TestPlanScriptBriefResultModel;
import org.bench4q.share.models.master.statistics.ScriptBehaviorsBriefModel;
import org.bench4q.share.models.master.statistics.ScriptBriefResultModel;
import org.bench4q.share.models.master.statistics.ScriptPagesBriefModel;
import org.bench4q.web.init.TestResultHandle;
import org.bench4q.web.masterMessager.TestPlanMessager;
import org.bench4q.web.model.ResultInfoModel;
import org.bench4q.web.model.TestBriefStatusModel;
import org.bench4q.web.model.TestPlanRequestModel;
import org.bench4q.web.model.UrlContentTypeDistributionModel;
import org.bench4q.web.service.TestPlanService;
import org.bench4q.web.validation.TestPlanValidate;
import org.bench4q.web.validation.ValidateResponseModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;

@Controller
@SessionAttributes({ "accessToken" })
public class TestPlanController extends BaseController {
	private TestPlanMessager testPlanMessager;

	private TestPlanValidate testPlanValidate;

	private TestPlanService testPlanService;

	private final int monitorPort = 5556;

	private TestPlanMessager getTestPlanMessager() {
		return testPlanMessager;
	}

	@Autowired
	private void setTestPlanMessager(TestPlanMessager testPlanMessager) {
		this.testPlanMessager = testPlanMessager;
	}

	private TestPlanValidate getTestPlanValidate() {
		return testPlanValidate;
	}

	@Autowired
	private void setTestPlanValidate(TestPlanValidate testPlanValidate) {
		this.testPlanValidate = testPlanValidate;
	}

	private TestPlanService getTestPlanService() {
		return testPlanService;
	}

	@Autowired
	private void setTestPlanService(TestPlanService testPlanService) {
		this.testPlanService = testPlanService;
	}

	@RequestMapping(value = "runTestPlan", method = RequestMethod.POST)
	@ResponseBody
	public Map<String, Object> runTestPlan(
			@ModelAttribute("accessToken") String accessToken,
			@RequestBody TestPlanRequestModel testPlanRequestModel) {
		Map<String, Object> map = new HashMap<String, Object>();
		ValidateResponseModel validateResponseModel = this
				.getTestPlanValidate().validateTestPlanSubmited(
						testPlanRequestModel);
		if (!validateResponseModel.isSuccess()) {
			return fail(map, validateResponseModel.getMessage());
		} else {
			TestPlanModel testPlanModel = this.getTestPlanService()
					.createTestPlan(testPlanRequestModel, monitorPort);
			String testPlanContent;
			try {
				testPlanContent = MarshalHelper.marshal(TestPlanModel.class,
						testPlanModel);

			} catch (JAXBException e) {
				Logger.getLogger(TestPlanController.class).info(
						ExceptionLog.getStackTrace(e));
				return fail(map, "error testPlan");
			}
			TestPlanResultModel testPlanResultModel = this
					.getTestPlanMessager().runTestPlan(accessToken,
							testPlanContent);
			if (testPlanResultModel != null
					&& testPlanResultModel.getTestPlanId() != null) {
				map = success(map);
				map.put("testPlanResult", testPlanResultModel);
				return map;
			} else {
				return fail(map, "empty test plan id");
			}
		}
	}

	@RequestMapping("/{testPlanId}/runningInfo")
	@ResponseBody
	public Map<String, Object> getRunningInfo(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId) {
		Map<String, Object> map = new HashMap<String, Object>();
		TestPlanResultModel testPlanResultModel = this.getTestPlanMessager()
				.getRunningTestInfo(accessToken, testPlanId);
		
		if (testPlanResultModel == null) {
			return fail(map, "");
		} else {
			success(map);
			map.put("testPlan", testPlanResultModel);
			return map;
		}
	}

	@RequestMapping(value = "/{testPlanId}/queryTestPlan")
	@ResponseBody
	public Map<String, Object> queryTestPlan(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId) {
		Map<String, Object> map = new HashMap<String, Object>();
		TestPlanDBModel testPlanResultModel = this.getTestPlanMessager()
				.queryTestPlanById(accessToken, testPlanId);
		if (testPlanResultModel != null) {
			success(map);
			map.put("testPlan", testPlanResultModel);
			return map;
		} else {
			return fail(map, "");
		}
	}

	@RequestMapping("/{testPlanId}/testResultHanadle")
	@ResponseBody
	public Map<String, Object> getTestResultHandle(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId) {
		Map<String, Object> map = new HashMap<String, Object>();
		TestPlanResultModel testPlanResultModel = this.getTestPlanMessager()
				.getRunningTestInfo(accessToken, testPlanId);
		if (testPlanResultModel == null) {
			return fail(map, "");
		}
		try {
			List<ResultInfoModel> results = new LinkedList<ResultInfoModel>();
			List<ResultInfoModel> scriptResultInfoModels = TestResultHandle.scriptResultInfoModel;
			List<ResultInfoModel> sutResultInfoModels = TestResultHandle.sutInfoModel;

			for (ScriptHandleModel scriptHandleModel : testPlanResultModel
					.getScripts()) {
				if (scriptHandleModel == null) {
					continue;
				}
				results.add(getScriptResultInfoModels(scriptHandleModel,
						scriptResultInfoModels));
			}
			if (testPlanResultModel.getMonitorModels() != null) {
				for (MonitorModel monitorModel : testPlanResultModel
						.getMonitorModels()) {
					results.add(getSUTResultInfoModel(monitorModel,
							sutResultInfoModels));
				}
			}

			map.put("resultHandle", results);
			success(map);
			return map;
		} catch (Exception e) {
			Logger.getLogger(this.getClass()).info(
					ExceptionLog.getStackTrace(e));
			return fail(map, "");
		}
	}

	private ResultInfoModel getScriptResultInfoModels(
			ScriptHandleModel scriptHandleModel,
			List<ResultInfoModel> scriptResultModels) {
		ResultInfoModel resultInfoModel = new ResultInfoModel();
		resultInfoModel.setName(scriptHandleModel.getScriptName());
		resultInfoModel.setValue(scriptHandleModel.getScriptId().toString());
		resultInfoModel.setChildren(scriptResultModels);
		return resultInfoModel;
	}

	private ResultInfoModel getSUTResultInfoModel(MonitorModel monitorModel,
			List<ResultInfoModel> resultInfoModels) {
		ResultInfoModel resultInfoModel = new ResultInfoModel();
		resultInfoModel.setName(monitorModel.getHostName());
		resultInfoModel.setValue(monitorModel.getHostName());
		resultInfoModel.setChildren(resultInfoModels);
		return resultInfoModel;
	}

	@RequestMapping("/{testPlanId}/{scriptId}/scriptBriefResult/{fieldName}/{startTime}")
	@ResponseBody
	public Map<String, Object> getScriptBriefStatus(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId, @PathVariable String scriptId,
			@PathVariable String fieldName, @PathVariable String startTime) {
		Map<String, Object> map = new HashMap<String, Object>();
		TestPlanScriptBriefResultModel testPlanScriptBriefResultModel = this
				.getTestPlanMessager().getScriptBriefResult(accessToken,
						testPlanId, scriptId, startTime);
		Logger.getLogger(this.getClass()).info(
				MarshalHelper.tryMarshal(testPlanScriptBriefResultModel));
		if (testPlanScriptBriefResultModel != null) {
			success(map);
			map.put("result",
					this.getTestPlanService().extractResultModelList(
							testPlanScriptBriefResultModel, fieldName));
			return map;

		} else {
			return fail(map, "");
		}
	}

	@RequestMapping("/{testPlanId}/testBriefStatus")
	@ResponseBody
	public Map<String, Object> getTestBriefStatus(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId) {
		Map<String, Object> map = new HashMap<String, Object>();
		TestBriefStatusModel testBriefStatusModel = new TestBriefStatusModel();
		long vus = 0;
		long requests = 0;
		TestPlanResultModel testPlanResultModel = this.getTestPlanMessager()
				.getRunningTestInfo(accessToken, testPlanId);

		if (testPlanResultModel == null) {
			return fail(map, "");
		}
		for (ScriptHandleModel scriptHandleModel : testPlanResultModel
				.getScripts()) {
			ScriptBriefResultModel lastScriptBriefResultModel = this
					.getTestPlanMessager().getLatestScriptBriefResult(
							accessToken, testPlanId,
							scriptHandleModel.getScriptId().toString());
			if (lastScriptBriefResultModel == null) {
				continue;
			}
			requests += lastScriptBriefResultModel.getTotalFailCountFromBegin()
					+ lastScriptBriefResultModel
							.getTotalSuccessCountFromBegin();
			vus += lastScriptBriefResultModel.getvUserCount();
		}
		testBriefStatusModel.setRequests(requests);
		testBriefStatusModel.setVus(vus);
		map.put("briefInfo", testBriefStatusModel);
		return success(map);
	}

	@RequestMapping("/{testPlanId}/{scriptId}/behaviors")
	@ResponseBody
	public Map<String, Object> getScriptBehaviorBriefModel(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId, @PathVariable String scriptId) {
		Map<String, Object> map = new HashMap<String, Object>();
		ScriptBehaviorsBriefModel scriptBehaviorsBriefModel = this
				.getTestPlanMessager().getScriptBehaviorsBriefResult(
						accessToken, testPlanId, scriptId);
		if (scriptBehaviorsBriefModel == null) {
			return fail(map, "");
		}
		map = success(map);
		map.put("behaviors", scriptBehaviorsBriefModel);
		return map;
	}

	@RequestMapping("/{testPlanId}/{scriptId}/page")
	@ResponseBody
	public Map<String, Object> getScriptPageBriefModel(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId, @PathVariable String scriptId) {
		Map<String, Object> map = new HashMap<String, Object>();
		ScriptPagesBriefModel scriptPagesBriefModel = this
				.getTestPlanMessager().getScriptPageBriefModel(accessToken,
						testPlanId, scriptId);

		if (scriptPagesBriefModel == null) {
			return fail(map, "");
		}
		map = success(map);
		map.put("pages", scriptPagesBriefModel);
		return map;
	}

	@SuppressWarnings("unchecked")
	@RequestMapping("/{testPlanId}/urlContentTypeDistribution")
	@ResponseBody
	public Map<String, Object> getUrlContentTypeDistribution(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId) {
		List<UrlContentTypeDistributionModel> urlContentTypeDistributionModels = new ArrayList<UrlContentTypeDistributionModel>();
		Map<String, Object> map = new HashMap<String, Object>();
		Map<String, Long> urlContentTypeCountMap = (Map<String, Long>) getUrlContentTypeCount(
				accessToken, testPlanId).get("result");
		if (urlContentTypeCountMap == null) {
			return fail(map, "");
		}
		Map<String, Double> urlContentTypePercentMap = this
				.getTestPlanService().getUrlContentTypePercent(
						urlContentTypeCountMap);
		Set<String> keys = urlContentTypeCountMap.keySet();
		Iterator<String> iterator = keys.iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			urlContentTypeDistributionModels
					.add(new UrlContentTypeDistributionModel(key,
							urlContentTypeCountMap.get(key),
							urlContentTypePercentMap.get(key)));
		}
		map = success(map);
		map.put("result", urlContentTypePercentMap);
		return map;

	}

	@SuppressWarnings("unchecked")
	@RequestMapping("/{testPlanId}/urlContentTypePercent")
	@ResponseBody
	public Map<String, Object> getUrlContentTypePercent(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId) {
		Map<String, Object> map = new HashMap<String, Object>();
		Map<String, Long> urlContentTypeCountMap = (Map<String, Long>) getUrlContentTypeCount(
				accessToken, testPlanId).get("result");
		if (urlContentTypeCountMap == null) {
			return fail(map, "");
		}
		map = success(map);
		map.put("result",
				this.getTestPlanService().getUrlContentTypePercent(
						urlContentTypeCountMap));
		return map;

	}

	@RequestMapping("/{testPlanId}/urlContentTypeCount")
	@ResponseBody
	public Map<String, Object> getUrlContentTypeCount(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanId) {

		Map<String, Object> map = new HashMap<String, Object>();
		List<BehaviorStatusCodeResultModel> behaviorStatusCodeResultModels = this
				.getTestPlanService().getAllBehaviorStatusCodeResultModels(
						accessToken, testPlanId);
		if (behaviorStatusCodeResultModels == null) {
			map.put("result", null);
			return fail(map, "");
		}
		Map<String, Long> urlContentCountMap = new HashMap<String, Long>();
		for (BehaviorStatusCodeResultModel behaviorStatusCodeResultModel : behaviorStatusCodeResultModels) {
			if (urlContentCountMap.get(behaviorStatusCodeResultModel
					.getContentType()) == null)
				urlContentCountMap.put(
						behaviorStatusCodeResultModel.getContentType(),
						behaviorStatusCodeResultModel.getCount());
			else {
				long originalValue = urlContentCountMap
						.get(behaviorStatusCodeResultModel.getContentType());
				urlContentCountMap.put(
						behaviorStatusCodeResultModel.getContentType(),
						originalValue
								+ behaviorStatusCodeResultModel.getCount());
			}

		}
		map = success(map);
		map.put("result", urlContentCountMap);
		return map;

	}

	@RequestMapping("/stop/{testPlanRunId}")
	@ResponseBody
	public Map<String, Object> stopRunningTestPlan(
			@ModelAttribute("accessToken") String accessToken,
			@PathVariable String testPlanRunId) {
		Map<String, Object> map = new HashMap<String, Object>();
		try {
			UUID.fromString(testPlanRunId);
		} catch (Exception e) {
			e.printStackTrace();
			return fail(map, "testPlanRunId is not a UUID");
		}
		TestPlanResponseModel testPlanResponseModel = this
				.getTestPlanMessager().stopTestPlan(accessToken, testPlanRunId);
		if (testPlanResponseModel.isSuccess()) {
			return success(map);
		} else {
			return fail(map, testPlanResponseModel.getFailCause());
		}
	}
	
	@RequestMapping("loadFilterTypeList")
	public @ResponseBody
	Map<String, Object> loadFilterTypeList(HttpServletRequest request,
			@ModelAttribute("accessToken") String accessToken) {
		TestPlanResponseModel responseModel = this.getTestPlanMessager().loadFilterTypeList(accessToken);
		Map<String,Object> map = new HashMap<String, Object>();
		if(responseModel.isSuccess()){
			map.put("filterTypeList", responseModel.getFilterTypeList());
			success(map);
		}else{
			fail(map, responseModel.getFailCause());
		}
		return map;
	}
}
