package com.eactive.eai.rms.onl.manage.adapter.adapter;

import java.io.File;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.SortDefault;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import com.eactive.eai.common.util.StringUtil;
import com.eactive.eai.common.util.SystemUtil;
import com.eactive.eai.rms.common.base.OnlBaseAnnotationController;
import com.eactive.eai.rms.common.combo.ComboService;
import com.eactive.eai.rms.common.combo.ComboVo;
import com.eactive.eai.rms.common.context.MonitoringContext;
import com.eactive.eai.rms.common.datasource.DataSourceType;
import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.common.login.SessionManager;
import com.eactive.eai.rms.common.spring.LocaleMessage;
import com.eactive.eai.rms.common.util.IpUtil;
import com.eactive.eai.rms.common.util.MonitoringSyncAgent;
import com.eactive.eai.rms.common.vo.GridResponse;
import com.eactive.eai.rms.data.entity.onl.adapter.AdapterGroupService;
import com.eactive.eai.rms.onl.loader.service.HistoryService;
import com.eactive.eai.rms.onl.loader.service.TransactionLoaderService;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterExportData;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUI;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterGroupUISearch;
import com.eactive.eai.rms.onl.manage.adapter.adapter.ui.AdapterUI;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 어뎁터 관리 화면 컨트롤러
 */
@Controller
public class AdapterController extends OnlBaseAnnotationController {

	private final LocaleMessage localeMessage;
	private final AdapterManService service;
	private final ComboService comboService;
	private final MonitoringContext monitoringContext;
	private final HistoryService historyService;
	private final TransactionLoaderService loaderService;
	private final ObjectMapper objectMapper;


	@Autowired
	public AdapterController(LocaleMessage localeMessage,
							AdapterManService service,
							ComboService comboService,
							MonitoringContext monitoringContext,
							MonitoringSyncAgent monitoringSyncAgent,
							HistoryService historyService,
							TransactionLoaderService loaderService,
							AdapterGroupService adapterGroupService,
							ObjectMapper objectMapper) {
		this.localeMessage = localeMessage;
		this.service = service;
		this.comboService = comboService;
		this.monitoringContext = monitoringContext;
		this.historyService = historyService;
		this.loaderService = loaderService;
		this.objectMapper = objectMapper;
	}


	private static Object obj = new Object();


	@GetMapping(value = "/onl/admin/adapter/adapterMan.view")
	public void view() {
		// view
	}

	@GetMapping(value = "/onl/admin/adapter/adapterMan.view", params = "cmd=DETAIL")
	public String viewDetail() {
		return "/onl/admin/adapter/adapterManDetail";
	}

	@GetMapping(value = "/onl/admin/adapter/adapterMan.view", params = "cmd=DETAILPOPUP")
	public String viewDetailPopup() {
		return "/onl/admin/adapter/adapterManDetailpopup";
	}

	@GetMapping(value = "/onl/admin/adapter/adapterMan.view", params = "cmd=POPUP CALENDAR")
	public String viewPopupCalendar() {
		return "/onl/admin/adapter/adapterManPopupCalendar";
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=LIST_SEARCH_INIT_COMBO")
	public ResponseEntity<Map<String, Object>> searchInitCombo() {

		List<ComboVo> listAdptrIoDstcd = comboService
								.getFromCode("ADPTR_IO_DSTCD");
		List<ComboVo> listAdptrMsgPtrnCd = comboService
								.getFromCode("ADPTR_MSG_PTRN_CD");
		List<ComboVo> listUseYn = comboService.getFromCode("USE_YN");
		List<ComboVo> listKesauseYn = comboService.getFromCode("KESA_YN");
		List<ComboVo> listTestMasterYn = comboService
								.getFromCode("TESTMASTER_YN");
		List<ComboVo> listAdptrCd = comboService.getFromAdapterType();
		List<ComboVo> listInoutTypeCd = comboService
								.getFromCode("ADPT_INOUT_TYPE");
		List<ComboVo> listBzwkCode = comboService.getFromBusinessType();
		List<ComboVo> listMsgType = comboService.getFromCode("MSG_TYPES");

		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("adptrIoDstcdRows", listAdptrIoDstcd);
		resultMap.put("adptrMsgPtrnCdRows", listAdptrMsgPtrnCd);
		resultMap.put("useYnRows", listUseYn);
		resultMap.put("kesauseYnRows", listKesauseYn);
		resultMap.put("testMasterYnRows", listTestMasterYn);
		resultMap.put("adptrCdRows", listAdptrCd);
		resultMap.put("inoutTypeCdRows", listInoutTypeCd);
		resultMap.put("bzwkCodeRows", listBzwkCode);
		resultMap.put("msgTypeRows", listMsgType);

		return ResponseEntity.ok(resultMap);
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=LIST_INIT_COMBO")
	public ResponseEntity<Map<String, Object>> initCombo() {

		List<ComboVo> listBiz = comboService.getFromBusinessType();
		List<ComboVo> listAdapterType = comboService.getFromAdapterType();
		List<ComboVo> listMsgType = comboService.getFromCode("MSG_TYPES");
		List<ComboVo> listMsgEncode = comboService.getFromCode("MSG_ENCODES");
		List<ComboVo> listInstance = comboService.getFromEAIServer();

		List<ComboVo> listAdptrIoDstcd = comboService
								.getFromCode("ADPTR_IO_DSTCD");
		List<ComboVo> listAdptrMsgPtrnCd = comboService
								.getFromCode("ADPTR_MSG_PTRN_CD");
		List<ComboVo> listUseYn = comboService.getFromCode("USE_YN");
		List<ComboVo> listYn = comboService.getFromCode("YN");
		List<ComboVo> listMonicondiSet = comboService
								.getFromCode("MONICONDI_SET");

		List<ComboVo> listYnNum = comboService.getFromCode("YN_NUM");
		List<ComboVo> listYnType = comboService.getFromCode("YN_TYPE");
		List<ComboVo> listTestMasterYn = comboService
								.getFromCode("TESTMASTER_YN");
		List<ComboVo> listInoutTypeCd = comboService
								.getFromCode("ADPT_INOUT_TYPE");

		String refClsName = monitoringContext.getStringProperty(
								MonitoringContext.RMS_ADAPTER_REFCLSNAME);

		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("bizRows", listBiz);
		resultMap.put("adapterTypeRows", listAdapterType);
		resultMap.put("msgTypeRows", listMsgType);
		resultMap.put("msgEncodeRows", listMsgEncode);
		resultMap.put("instanceRows", listInstance);

		resultMap.put("adptrIoDstcdRows", listAdptrIoDstcd);
		resultMap.put("adptrMsgPtrnCdRows", listAdptrMsgPtrnCd);
		resultMap.put("useYnRows", listUseYn);
		resultMap.put("ynRows", listYn);
		resultMap.put("monicondiSetRows", listMonicondiSet);
		resultMap.put("ynNumRows", listYnNum);
		resultMap.put("ynTypeRows", listYnType);
		resultMap.put("refClsName", refClsName);
		resultMap.put("testMasterYnRows", listTestMasterYn);
		resultMap.put("inoutTypeRows", listInoutTypeCd);

		return ResponseEntity.ok(resultMap);
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=LIST_PROP")
	public ResponseEntity<Map<String, Object>> selectPropList(String adptrCd,
							String adptrIoDstcd) {
		Map<String, Object> resultMap = service.selectListAdapterProp(adptrCd,
								adptrIoDstcd);
		return ResponseEntity.ok(resultMap);
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=LIST_DOMAIN")
	public ModelAndView selectDomainList(HttpServletRequest request,
							HttpServletResponse response, String domainName) {

		List list = service.selectListDomain(domainName);

		Map resultMap = new HashMap();
		resultMap.put("rows", list);

		ModelAndView modelAndView = new ModelAndView("jsonView", resultMap);
		return modelAndView;
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=LIST")
	public ResponseEntity<GridResponse<AdapterGroupUI>> selectList(
							@SortDefault("adptrbzwkgroupname") Pageable pageable,
							AdapterGroupUISearch adapterGroupUISearch) {
		Page<AdapterGroupUI> page = service.selectList(pageable,
								adapterGroupUISearch);
		return ResponseEntity.ok(new GridResponse<>(page));
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=DETAIL")
	public ResponseEntity<AdapterGroupUI> selectDetail(
							@RequestParam(name = "ADPTRBZWKGROUPNAME") String adptrbzwkgroupname) {

		AdapterGroupUI adapterGroupUIDetail = service
								.selectDetail(adptrbzwkgroupname);
		return ResponseEntity.ok(adapterGroupUIDetail);
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=INSERT")
	public ResponseEntity<Map<String, String>> insert(
							AdapterGroupUI adapterGroupUI) throws Exception {

		synchronized (obj) {
			service.insert(adapterGroupUI);

			Map<String, String> resultMap = service.reloadSync(adapterGroupUI);
			return ResponseEntity.ok(resultMap);
		}

	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=UPDATE")
	public ResponseEntity<Map<String, String>> update(
							AdapterGroupUI adapterGroupUI) throws Exception {

		synchronized (obj) {
			service.update(adapterGroupUI);

			Map<String, String> resultMap = service.reloadSyncStdMsgAndAdapter(adapterGroupUI);
			return ResponseEntity.ok(resultMap);
		}

	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=DELETE")
	public ResponseEntity<Map<String, String>> delete(
							AdapterGroupUI adapterGroupUI) throws Exception {

		synchronized (obj) {
			List<AdapterUI> list = service.selectListAdapter(
									adapterGroupUI.getAdptrbzwkgroupname());
			service.delete(adapterGroupUI);

			Map<String, String> resultMap = service.deleteSync(adapterGroupUI,
									list);
			return ResponseEntity.ok(resultMap);
		}

	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=CLONE")
	public ResponseEntity<Void> clone(AdapterGroupUI adapterGroupUI)
							throws Exception {

		synchronized (obj) {
			// clone은 사용여부를 필수로 변경해야 되기 때문에 여기서 동기화는 필요없음
			service.insertClone(adapterGroupUI);
		}

		return ResponseEntity.ok().build();
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=UPDATE_OSIDINSTIINFO")
	public ResponseEntity<Map<String, Object>> updateAdptOsidinstiInfo(
							AdapterGroupUI adapterGroupUI) {

		String message = "";

		service.updateAdptOsidinstiInfo(adapterGroupUI);

		Map<String, Object> resultMap = new HashMap<>();
		resultMap.put("message", message);

		return ResponseEntity.ok(resultMap);

	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.excel", params = "cmd=LIST_GRID_TO_EXCEL")
	public ModelAndView listGridToExcel(
							@RequestParam Map<String, Object> paramMap,
							AdapterGroupUISearch adapterGroupUISearch)
							throws JsonProcessingException {
		Map<String, Object> resultMap = new HashMap<>();

		String[] headers = objectMapper.readValue(
								(String) paramMap.get("header"),
								String[].class);
		String[] headerTitles = objectMapper.readValue(
								(String) paramMap.get("headerTitle"),
								String[].class);

		Map<String, Object>[] datas = service
								.selectListToExcel(adapterGroupUISearch);

		HashMap<String, Object>[] lmerge = null;
		String merge = (String) paramMap.get("merge");

		if (merge != null) {
			lmerge = objectMapper.readValue(merge,
									new TypeReference<HashMap<String, Object>[]>() {});
		}

		resultMap.put("headers", headers);
		resultMap.put("headerTitles", headerTitles);
		resultMap.put("datas", datas);
		resultMap.put("fileName", paramMap.get("fileName"));
		resultMap.put("merge", lmerge);
		return new ModelAndView("excelView", resultMap);
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=CLONETOPROD")
	public ModelAndView cloneToProd(HttpServletRequest request,
							@RequestParam HashMap<String, Object> paramMap)
							throws Exception {

		if (SystemUtil.isProdServer()) {
			throw new Exception("only support dev, stage server");
		}

		HashMap<String, Object> resultMap = new HashMap<>();
		String userId = SessionManager.getUserId(request);
		String serviceKey = SessionManager.getServiceTypeKey(request);
		String cmTitle = MapUtils.getString(paramMap, "CmTitle",
								"Deploy Adapter");
		String cmDate = StringUtil.getNullStr(paramMap.get("CmDate"));
		String cmTime = StringUtil.getNullStr(paramMap.get("CmTime"));
		String listAdapterName = StringUtil
								.getNullStr(paramMap.get("ListAdapterName"));
		String[] adapterNames = listAdapterName.split("\\,");
		String downloadPath = monitoringContext.getStringProperty(
								MonitoringContext.IOMAP_DOWNLOAD_PATH);
		downloadPath = StringUtils.removeEnd(downloadPath, File.separator)
								+ File.separator + serviceKey
								+ File.separator;
		String targetName = monitoringContext.getStringProperty(
								MonitoringContext.IOMAP_DEPLOY_TARGET_NAME);
		String targetUrl = monitoringContext.getStringProperty(
								MonitoringContext.IOMAP_DEPLOY_TARGET_URL);
		targetUrl = StringUtils.removeEnd(targetUrl, "/");

		String userIp = IpUtil.getClientIp(request);

		if (logger.isInfoEnabled()) {
			logger.info("-------------------------------------------");
			logger.info("userId=" + userId);
			logger.info("userIp=" + userIp);
			logger.info("serviceKey=" + serviceKey);
			logger.info("cmDate=" + cmDate);
			logger.info("cmTime=" + cmTime);
			logger.info("listAdapterName=" + listAdapterName);
			logger.info("downloadPath=" + downloadPath);
			logger.info("targetName=" + targetName); // 스테이징, 운영서버
			logger.info("targetUrl=" + targetUrl); // http://127.0.0.1:20100
			logger.info("-------------------------------------------");
		}

		DataSourceType dt = DataSourceTypeManager.getDataSourceType(serviceKey);
		String items = "";

		// 0. 서브 배포 목록 확보
		HashMap<String, ArrayList<HashMap<String, String>>> listMap = historyService
								.userHistoryListByAdapter(dt, adapterNames);

		if (listMap != null && listMap.size() > 0) {

			// 1. 파일 생성
			try {
				loaderService.makeDataFiles(dt, downloadPath, listMap);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				throw new Exception("Make Datafile error:" + e.getMessage());
			}

			// 2. 파일 배포
			try {

				if (StringUtils.isBlank(targetUrl)) {
					throw new Exception("Set EMS property:"
											+ MonitoringContext.IOMAP_DEPLOY_TARGET_URL);
				}

				String url = targetUrl + "/monitoring/loader.do";
				loaderService.deployDataToTargetSystem(url, downloadPath,
										listMap, serviceKey);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
				throw new Exception("Deploy to target System(" + targetName
										+ ") error:" + e.getMessage());
			}

			items += historyService.parseSimple(serviceKey, "", listMap);
		}

		// 3. 데이터 적용 또는 스케쥴 등록
		try {
			String url = targetUrl + "/monitoring/loader.do";
			loaderService.scheduleDeployDataApply(url, userId, userIp,
									serviceKey, cmTitle, cmDate, cmTime, items);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new Exception("Apply DeployData or Apply Schedule error:"
									+ e.getMessage());
		}

		resultMap.put("message", localeMessage.getMessage("ruleDeploy.msg4"));

		ModelAndView modelAndView = new ModelAndView("jsonView", resultMap);
		return modelAndView;
	}


	@PostMapping(value = "/onl/admin/adapter/adapterMan.file", params = "cmd=LIST_IMPORT_FILE")
	public ResponseEntity<?> listImportFromFile(
							@RequestParam("file") MultipartFile file, boolean importDupliCheck) {

		try {

			if (file != null) {
				String extension = FilenameUtils.getExtension(
										file.getOriginalFilename())
										.toLowerCase();

				if ("json".equals(extension)) {

					synchronized (obj) {
						AdapterGroupUI importedUI = service
												.importAdapterFromJson(file, importDupliCheck);

						Map<String, String> resultMap = service
												.reloadSync(importedUI);
						return ResponseEntity.ok(resultMap);
					}

				}

			}

		} catch (IllegalStateException dupEx) {
			return ResponseEntity
					.status(HttpStatus.CONFLICT)
					.body(dupEx.getMessage());
		} catch (Exception e) {
			logger.error(e);
			return ResponseEntity.internalServerError()
									.body("Error processing file");
		}

		return ResponseEntity.internalServerError()
								.body("empty file or unmatched extension");
	}

	@PostMapping(value = "/onl/admin/adapter/adapterMan.json", params = "cmd=DETAIL_EXPORT_TO_JSON")
	public ResponseEntity<byte[]> detailExportToJson(
							@RequestParam(name = "adapterGroupName") String adapterGroupName)
							throws Exception {
		AdapterExportData exportData = service
								.exportAdapterToJson(adapterGroupName);

		AdapterGroupUI selectedUI = exportData.getAdapterGroupUI();
		String fileName = adapterGroupName + "("
								+ selectedUI.getAdptrbzwkgroupdesc() + ").json";
		fileName = FilenameUtils.normalize(fileName);
		fileName = URLEncoder.encode(fileName, "UTF-8");

		HttpHeaders headers = new HttpHeaders();
		headers.add(HttpHeaders.CONTENT_DISPOSITION,
								"attachment; filename=\"" + fileName + "\"");
		headers.add(HttpHeaders.CONTENT_TYPE, "application/json");

		return ResponseEntity.ok()
								.headers(headers)
								.body(exportData.getExportFileContent());
	}
}
