package org.bench4q.master.api;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.bench4q.master.domain.entity.TestPlan;
import org.bench4q.master.domain.factory.BusinessModelMapFactory;
import org.bench4q.master.domain.service.TestPlanEngine;
import org.bench4q.master.domain.service.TestPlanScriptResultService;
import org.bench4q.master.domain.service.TestPlanService;
import org.bench4q.master.domain.service.UserService;
import org.bench4q.master.domain.service.report.ReportService;
import org.bench4q.master.exception.Bench4QException;
import org.bench4q.master.exception.Bench4QRunTimeException;
import org.bench4q.share.enums.master.TestPlanStatus;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

@Controller
@RequestMapping("/testPlan")
public class TestPlanController extends BaseController {
	private TestPlanEngine testPlanRunner;
	private TestPlanService testPlanService;
	private ReportService reportService;
	private TestPlanScriptResultService testPlanScriptResultService;
	private BusinessModelMapFactory businessMapFactory;
	private Logger logger = Logger.getLogger(TestPlanController.class);

	private TestPlanEngine getTestPlanRunner() {
		return testPlanRunner;
	}

	@Autowired
	private void setTestPlanRunner(TestPlanEngine testPlanRunner) {
		this.testPlanRunner = testPlanRunner;
	}

	private TestPlanService getTestPlanService() {
		return this.testPlanService;
	}

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

	private ReportService getReportService() {
		return reportService;
	}

	@Autowired
	private void setReportService(ReportService reportService) {
		this.reportService = reportService;
	}

	private BusinessModelMapFactory getBusinessMapFactory() {
		return businessMapFactory;
	}

	@Autowired
	private void setBusinessMapFactory(
			BusinessModelMapFactory businessMapFactory) {
		this.businessMapFactory = businessMapFactory;
	}

	private TestPlanScriptResultService getTestPlanScriptResultService() {
		return testPlanScriptResultService;
	}

	@Autowired
	private void setTestPlanScriptResultService(
			TestPlanScriptResultService testPlanScriptResultService) {
		this.testPlanScriptResultService = testPlanScriptResultService;
	}

	@RequestMapping(value = "/run", method = {
			RequestMethod.POST, RequestMethod.GET })
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public TestPlanResultModel runTestPlanWithTestPlanModel(
			@RequestBody TestPlanModel testPlanBusinessModel)
			throws Bench4QException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			throw new Bench4QException(HAVE_NO_POWER,
					"You don't have enough power to run a test plan!",
					"/run");
		}
		UUID testPlanRunID = this.getTestPlanRunner().runWith(
				testPlanBusinessModel, this.getPrincipal());
		if (testPlanRunID == null) {
			throw new Bench4QException("TestPlan_Commit_Error",
					"There is an exception when commit the test plan",
					"/run");
		}
		return buildResponseModel(this.getTestPlanService()
				.queryTestPlanStatus(testPlanRunID), testPlanRunID, null,
				testPlanBusinessModel.getMonitorModels());
	}

	private TestPlanResultModel buildResponseModel(
			TestPlanStatus currentStatus, UUID testPlanId,
			List<ScriptHandleModel> scripts, List<MonitorModel> monitorModels) {
		TestPlanResultModel result = new TestPlanResultModel();
		result.setCurrentStatus(currentStatus);
		result.setTestPlanId(testPlanId);
		result.setScripts(scripts);
		result.setMonitorModels(monitorModels);
		return result;
	}

	@RequestMapping(value = "/getRunningInfo", method = { RequestMethod.GET,
			RequestMethod.POST })
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public TestPlanResultModel getTestPlanRunningInfo(
			@RequestParam UUID testPlanId) throws Bench4QException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			throw new Bench4QException(HAVE_NO_POWER,
					"You have not power to get test plan running info",
					"/getRunningInfo");
		}

		return this.getTestPlanService().buildResultModel(testPlanId);
	}

	@RequestMapping(value = "/scriptBrief/{testPlanId}/{scriptId}/{duationBegin}", method = RequestMethod.GET)
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public TestPlanScriptBriefResultModel getScriptBrief(
			@PathVariable UUID testPlanId, @PathVariable int scriptId,
			@PathVariable long duationBegin) throws Bench4QException,
			NullPointerException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			throw new Bench4QException(HAVE_NO_POWER,
					"You have not power to get test plan script brief",
					"/getRunningInfo");
		}
		List<ScriptBriefResultModel> scriptBriefResultModels = this
				.getTestPlanScriptResultService().loadScriptBriefWithDuation(
						testPlanId, scriptId, duationBegin);
		System.out.println("Script Result Size : "
				+ scriptBriefResultModels.size());
		TestPlanScriptBriefResultModel ret = new TestPlanScriptBriefResultModel();
		ret.setScriptBriefResultModels(scriptBriefResultModels);
		return ret;
	}

	@RequestMapping(value = "/getBehaviorsBrief/{testPlanRunID}/{scriptId}")
	@ResponseBody
	public ScriptBehaviorsBriefModel getBehaviorsBrief(
			@PathVariable UUID testPlanRunID, @PathVariable int scriptId)
			throws Bench4QException, NullPointerException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			throw new Bench4QException(HAVE_NO_POWER, EXCEPTION_HAPPEND
					+ "when get behaviors's brief", "/getBehaviorsBrief");
		}
		ScriptBehaviorsBriefModel result = this
				.getTestPlanScriptResultService()
				.getLatestScriptBehaviorsBrief(testPlanRunID, scriptId);
		return result;
	}

	@RequestMapping(value = "/pagesBrief/{testPlanRunId}/{scriptId}")
	@ResponseBody
	public ScriptPagesBriefModel getPagesBrief(
			@PathVariable UUID testPlanRunId, @PathVariable int scriptId)
			throws Bench4QException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			throw new Bench4QException(HAVE_NO_POWER, EXCEPTION_HAPPEND
					+ "when get behaviors's brief", "/getBehaviorsBrief");
		}
		ScriptPagesBriefModel pagesBriefModel = this
				.getTestPlanScriptResultService().getLatestScriptPagesBrief(
						testPlanRunId, scriptId);
		return pagesBriefModel;
	}

	@RequestMapping(value = "/loadTestPlans", method = { RequestMethod.GET,
			RequestMethod.POST })
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public TestPlanResponseModel loadTestPlans() {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			return buildTestPlanResponseModel(false, "no scope", null,null);
		}
		List<TestPlan> testPlanDBs = this.testPlanService.loadTestPlans(this
				.getPrincipal());
		return testPlanDBs == null ? buildTestPlanResponseModel(false,
				"exception", null,null) : buildTestPlanResponseModel(true, null,
				testPlanDBs,null);
	}

	@RequestMapping(value = "/queryTestPlan/{runId}", method = RequestMethod.GET)
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public TestPlanDBModel queryTestPlan(@PathVariable UUID runId)
			throws Bench4QException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			throw new Bench4QException(HAVE_NO_POWER, HAVE_NO_POWER,
					"/queryTestPlan/{runId}");
		}
		return this.getTestPlanService().getTestPlanDBModel(runId);
	}

	@RequestMapping(value = "/removeTestPlanFromPool", method = {
			RequestMethod.GET, RequestMethod.POST })
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public TestPlanResponseModel removeTestPlanFromPool(int testPlanId) {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			return buildTestPlanResponseModel(false, "no scope", null,null);
		}
		return buildTestPlanResponseModel(
				this.testPlanService.removeTestPlanInDB(testPlanId), null, null,null);
	}

	private TestPlanResponseModel buildTestPlanResponseModel(boolean success,
			String failCause, List<TestPlan> testPlanDBs,String[] filterTypeList) {
		TestPlanResponseModel result = new TestPlanResponseModel();
		result.setSuccess(success);
		result.setFailCause(failCause);
		List<TestPlanDBModel> modelList = new ArrayList<TestPlanDBModel>();
		if (testPlanDBs != null) {
			for (TestPlan testPlanDB : testPlanDBs) {
				modelList.add(this.getBusinessMapFactory().toModel(testPlanDB));
			}
		}
		result.setTestPlanDBModels(modelList);
		result.setFilterTypeList(filterTypeList);
		return result;
	}

	@RequestMapping(value = "/getTestPlanReport", method = { RequestMethod.GET,
			RequestMethod.POST })
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public void getTestPlanReport(HttpServletResponse response,
			@RequestParam UUID testPlanRunID) throws Bench4QException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			throw new Bench4QException(HAVE_NO_POWER,
					"You have no power to get test plan report",
					"/getTestPlanReport");
		}
		buildFileStream(response, testPlanRunID, this.getReportService()
				.createReport(testPlanRunID));
	}

	private void buildFileStream(HttpServletResponse response,
			UUID testPlanRunID, byte[] pdfBuffer) {
		try {
			ServletOutputStream outputStream = response.getOutputStream();
			response.reset();
			response.setHeader("Content-disposition", "attachment; filename=\""
					+ testPlanRunID.toString() + ".pdf\"");
			response.addHeader("Content-Length", "" + pdfBuffer.length);
			this.logger.info("report of test plan " + testPlanRunID.toString()
					+ " length is: " + pdfBuffer.length);
			response.setContentType("application/pdf");
			outputStream.write(pdfBuffer);
			outputStream.flush();
			outputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@RequestMapping(value = "/scriptBrief/{testPlanId}/{scriptId}/latestResult")
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public ScriptBriefResultModel getLatestScriptResult(
			@PathVariable UUID testPlanId, @PathVariable int scriptId)
			throws Bench4QException {
		guardHasAuthentication(
				"You have not power to get test plan script brief",
				"/getLatestScriptResult");
		return this.getTestPlanScriptResultService()
				.getLatestScriptBriefResultModel(testPlanId, scriptId);

	}

	@RequestMapping(value = "/stop/{testPlanId}")
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public TestPlanResponseModel stop(@PathVariable UUID testPlanId)
			throws Bench4QException {
		guardHasAuthentication("You have no power to stop test plan",
				"/stop/{testPlanId}");
		guardIsTheOwner(testPlanId);
		return buildTestPlanResponseModel(
				this.getTestPlanRunner().stop(testPlanId), "",
				Collections.<TestPlan> emptyList(),null);
	}

	private void guardIsTheOwner(UUID testPlanId) {
		if (!getPrincipal().getUserName().equals(
				this.getTestPlanService().getTestPlanDBModel(testPlanId)
						.getUserModel().getUserName())) {
			throw new Bench4QRunTimeException("You are not the owner");
		}
	}
	
	@RequestMapping(value = "/loadFilterTypeList", method = { RequestMethod.POST,
			RequestMethod.GET })
	@ResponseStatus(value = HttpStatus.OK)
	@ResponseBody
	public TestPlanResponseModel loadFilterTypeList() throws Bench4QException {
		if (!this.checkScope(UserService.NORAML_AUTHENTICATION)) {
			throw new Bench4QException(HAVE_NO_POWER,
					"You don't have enough power to get filter type list!",
					"/loadFilterTypeList");
		}
		String[] filterTypeList = this.getTestPlanService().loadFilterTypeList();
		return this.buildTestPlanResponseModel(true, null, null,filterTypeList);
	}
}
