package cokr.xit.fims.crdn.service.bean;

import java.io.File;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import cokr.xit.base.file.FileInfo;
import cokr.xit.base.file.service.bean.FileBean;
import cokr.xit.base.file.web.FileInfoFactory;
import cokr.xit.base.user.dao.UserMapper;
import cokr.xit.fims.cmmn.CmmnUtil;
import cokr.xit.fims.crdn.Crdn;
import cokr.xit.fims.crdn.CrdnQuery;
import cokr.xit.fims.crdn.Team;
import cokr.xit.fims.crdn.dao.CrdnTeamMapper;
import cokr.xit.fims.crdn.receive.eqpmnt.SingleFileParser;
import cokr.xit.fims.crdn.service.CrdnService;
import cokr.xit.fims.crdn.service.ImportService;
import cokr.xit.fims.mngt.service.bean.TaskProcessor;
import cokr.xit.foundation.component.AbstractServiceBean;
import cokr.xit.foundation.data.DataObject;
import cokr.xit.interfaces.smg.Petition;
import cokr.xit.interfaces.smg.service.bean.SmgReceiverBean;

/**외부 연계 파일에서 단속자료를 추출하여 등록하는 서비스의 구현체
 * @author mjkhan
 */
//@Service("importService")
public class ImportServiceBean2 extends AbstractServiceBean implements ImportService {
	@Resource(name = "smgReceiverBean")
	private SmgReceiverBean smgBean;
	@Resource(name = "importBean")
	private ImportBean importBean;
	@Resource(name = "fileBean")
	private FileBean fileBean;
	@Resource(name = "crdnService")
	private CrdnService crdnService;
	@Resource(name = "crdnBean")
	private CrdnBean crdnBean;
	@Resource(name = "crdnStngBean")
	private CrdnStngBean crdnStngBean;

	@Resource(name="userMapper")
	protected UserMapper userMapper;
	@Resource(name = "crdnTeamMapper")
	private CrdnTeamMapper crdnTeamMapper;

	@Override
	public int createCrdns(List<String> interfaceSequences) {
		List<Petition> petitions = smgBean.getPetitions(interfaceSequences);
		List<Crdn> crdns = importBean.createCrdns(petitions);
		if (crdns.isEmpty()) return 0;

		List<String> intfSeqs = crdns.stream().map(Crdn::getCvlcptLinkId).toList();

		List<FileInfo> smgFileInfoList = fileBean.getFilesOf("010", intfSeqs.toArray(new String[intfSeqs.size()]));
		Map<String, List<FileInfo>> byInfoKey = smgFileInfoList.stream().collect(Collectors.groupingBy(FileInfo::getInfoKey));

		for (Crdn crdn: crdns) {
			List<FileInfo> files = byInfoKey.get(crdn.getCvlcptLinkId());

			files.removeIf(item -> item.getMimeType().startsWith("video"));

			for (FileInfo file : files) {
		    	file.setInputStream(file.getInputStream()); //신규 인풋스트림 생성
		    	file.setUrl(""); //인터페이스 첨부파일정보 제거
		    	file.setPath(""); //국민신문고 첨부파일정보 제거
			}

		    crdn.setCvlcptLinkYn("Y");
			crdnBean.create(null, crdn, files);
		}

		importBean.createCvlcpts(petitions);
		return 0;
	}

	@Override
	public Map<String, Object> createCrdnByEquipmentLinkFile(Map<String,String> processInfo, List<DataObject> fileList) {
		String workPath = processInfo.get("workPath");
		String taskSeCd = processInfo.get("taskSeCd");
		String orgID = currentUser().getOrgID();
		String fileGroupType = processInfo.get("fileGroupType");
		boolean statByTeamEquipment = TaskProcessor.get().statByTeamEquipment(taskSeCd),
				excludeExempted = "true".equals(processInfo.get("excludeExempted")),
				cctv = isEmpty(workPath);

		List<DataObject> divided = divideFiles(fileList);
		List<DataObject> errors = divided.stream().filter(info -> !isEmpty(info.get("error"))).toList();
		divided.removeAll(errors); // 오류 있는 항목들을 제외
		log().debug("divided: {}, errors: {}", divided.size(), errors.size());
		errors.forEach(row -> log().debug("error: {}", row));
		List<DataObject> crdnList = divided.stream()
			.map(dividedInfo -> {
				DataObject result = new DataObject();
				DataObject metaInfo = (DataObject)dividedInfo.get("metaInfo");

				List<DataObject> imageList = CmmnUtil.getDataObjectListFromMap(dividedInfo,"imageInfo");
				DataObject bgngData = (DataObject)dividedInfo.get("bgngData");
				DataObject endData = (DataObject)dividedInfo.get("endData");

				//1. 연계파일정보를 단속 엔티티로 변환
				Crdn crdn = new Crdn();
				crdn.setCrdnRegSeCd("07");
				crdn.setTaskSeCd(taskSeCd);
				crdn.setSggCd(orgID);

				crdnTimeRangeSet(crdn, bgngData, endData);

				String crdnInptSeCd = switch (blankIfEmpty(metaInfo.get("LINK_ENT_NM"))) {
				case "ino" -> "11";
				case "knl" -> "12";
				default -> "15";
				};
				crdn.setCrdnInptSeCd(crdnInptSeCd);
				crdn.setVltnCd(ifEmpty(metaInfo.string("VLTN_CD"), () -> "01"));
				crdn.setCrdnSpareaCd(ifEmpty(metaInfo.string("CRDN_SPAREA_CD"), () -> "00"));

				crdn.setVhrno(metaInfo.string("VHRNO"));
				crdn.setCrdnYmd(metaInfo.string("CRDN_YMD"));
				crdn.setCrdnTm(metaInfo.string("CRDN_TM"));
				if (isEmpty(crdn.getCrdnBgngTm())) {
					crdn.setCrdnBgngTm(metaInfo.string("CRDN_BGNG_TM"));
					crdn.setCrdnEndTm(metaInfo.string("CRDN_END_TM"));
				}

				crdn.setGpsX(metaInfo.string("GPS_X"));
				crdn.setGpsY(metaInfo.string("GPS_Y"));
				crdn.setCrdnPlc(metaInfo.string("CRDN_PLC"));
				crdn.setCrdnRoadNm(metaInfo.string("CRDN_ROAD_NM"));
				crdn.setCrdnStdgNm(metaInfo.string("CRDN_STDG_NM"));

				crdn.setCrdnSeCd(metaInfo.string("CRDN_SE_CD"));
				crdn.setMoscX(metaInfo.string("MOSC_X"));
				crdn.setMoscY(metaInfo.string("MOSC_Y"));

				if (statByTeamEquipment) {
					if (!isEmpty(crdn.getCrdnSeCd()) && !isEmpty(metaInfo.string("EQPMNT_CD"))) {
						Team team = new Team();
						team.setSggCd(crdn.getSggCd());
						team.setTaskSeCd(taskSeCd);
						team.setTeamNm(metaInfo.string("EQPMNT_CD"));
						DataObject teamInfo = crdnTeamMapper.selectTeamInfoByName(team);

						if (teamInfo != null && crdn.getCrdnSeCd().equals(teamInfo.string("CRDN_SE_CD"))) {
							crdn.setTeamId(teamInfo.string("TEAM_ID"));
						}
					}
				}
				//metaInfo.string("PLATE_WIDTH");
				//metaInfo.string("PLATE_HEIGHT");
				List<FileInfo> fileInfos = parseDataInfoToFileInfo(imageList, ifEmpty(fileGroupType, () -> metaInfo.string("FILE_GROUP_TYPE")));
				result
					.set("crdn", crdn)
					.set("fileInfos", fileInfos)
					.set("files", cctv ? dividedInfo.get("files") : null);
				return result;
			})
			.toList();

		List<Crdn> crdns = crdnList.stream()
			.map(info -> (Crdn)info.get("crdn"))
			.toList();
		//2. 차적 조회
		importBean.setVehicleInfo(crdns);

		List<String>
			vhrnos = crdns.stream().map(crdn -> crdn.getVhrno()).toList(),
			exempted = crdnStngBean.selectExemptionVehicleList(new CrdnQuery().setVhrnos(vhrnos).setSggCd(orgID))
				.stream()
				.map(row -> row.string("VHRNO"))
				.toList();
		ArrayList<File> success = new ArrayList<>();
		HashMap<String, Object> resultMap = new HashMap<>();
		//3.등록
		crdnList.forEach(info -> {
			Crdn crdn = (Crdn)info.get("crdn");
			if (exempted.contains(crdn.getVhrno()) && excludeExempted)
				crdn.setCrdnSttsCd("81"); // 면제차량
			List<FileInfo> fileInfoList = (List<FileInfo>)info.get("fileInfos");
			boolean saved = crdnBean.create(null, crdn, fileInfoList);
			resultMap.put("saved", saved);
			if (saved) {
				if (!isEmpty(crdn.getErsrRegYmd())) {
					String vhrno = crdn.getVhrno();
					String ersrRegYmd = crdn.getErsrRegYmd();
					String ersrYear = ersrRegYmd.substring(0, 4);
					String ersrMonth = ersrRegYmd.substring(4, 6);
					String ersrDay = ersrRegYmd.substring(6, 8);

					resultMap.put("alertMessage", vhrno + " : " + ersrYear + "년" + ersrMonth + "월" + ersrDay + "일로 말소된 차량입니다.");
				}
				if (cctv) {
					List<DataObject> files = (List<DataObject>)info.get("files");
					if (!isEmpty(files))
						success.addAll(files.stream().map(file -> (File)file.get("file")).toList());
				}
			} else {
				resultMap.put("failReason", "기타 오류");
			}
		});
		if (!isEmpty(workPath))
			fileList.forEach(delInfo -> crdnBean.removeEquipmentLinkFile(workPath, delInfo.string("FILE_NAME")));

		String dir = null;
		for (File file: success) {
			String srcDir = file.getParentFile().getAbsolutePath(),
				   destDir = srcDir.replace(File.separator + "work", File.separator + "success");
			if (!equals(dir, destDir))
				ensureDir(dir = destDir);

			file.renameTo(new File(destDir + File.separator + file.getName()));
		}

		return resultMap;
	}

	/**파싱된 데이터 목록을 메타정보, 이미지정보목록, 최초단속자료, 최종단속자료로 분리한다. <br />
	 * @param fileList 연계파일정보목록, fileGroupType 연계파일그룹형식
	 * @return 메타정보, 이미지정보목록, 최초단속자료, 최종단속자료
	 */
	private List<DataObject> divideFiles(List<DataObject> fileList) {
		Map<String, List<DataObject>> byName = fileList.stream().collect(Collectors.groupingBy(fileInfo -> {
			String filename = fileInfo.string("FILE_NAME");
			return filename.substring(0, filename.lastIndexOf("_"));
		}));

		return byName.values().stream()
			.map(files -> {
				DataObject result = new DataObject();
				DataObject metaInfo = null;
				List<DataObject> imgs = null;
				DataObject[] bgngAndEnd = null;

				DataObject first = files.get(0);
				String groupType = first.string("FILE_GROUP_TYPE");

				if ("TXT".equalsIgnoreCase(groupType)) {
					Optional<DataObject> found = files.stream()
						.filter(file -> "TXT".equalsIgnoreCase(file.string("FILE_EXTENSION")))
						.findFirst();
					boolean present = found.isPresent();
					imgs = !present ? files : new ArrayList<>(files);
					if (present) {
						imgs.remove(metaInfo = found.get());
					}
					bgngAndEnd = getBgngDataAndEndData(imgs);
				} else if ("JPG".equalsIgnoreCase(groupType)) {
					bgngAndEnd = getBgngDataAndEndData(imgs = files);
					metaInfo = bgngAndEnd[1] == null ? first : bgngAndEnd[1];
				} else if ("BIN".equalsIgnoreCase(groupType)) {
					metaInfo = first;
					imgs = new ArrayList<>();
					for (int i = 1, otherPhotoCnt = toInt(metaInfo.get("PHOTO_CNT")); i <= otherPhotoCnt; i++){
						String fileName = metaInfo.string("B64IMAGE" + i + "_NM");
						String b64str = metaInfo.string("B64IMAGE" + i);
						DataObject dataObject = new DataObject()
							.set("B64IMAGE_NM", fileName)
							.set("B64IMAGE", b64str);
						imgs.add(dataObject);
					}
				}

				result
					.set("metaInfo", metaInfo)
					.set("imageInfo", imgs)
					.set("bgngData", bgngAndEnd[0])
					.set("endData", bgngAndEnd[1])
					.set("files", files);
				if (metaInfo == null)
					result.set("error", "metaInfo not found");

				return result;
			})
			.collect(Collectors.toCollection(ArrayList::new));
	}

	/**단속이미지정보 목록 내에서 최초단속자료와 최종단속자료를 반환한다. 단속일시 정보가 없을 경우 null을 반환한다. <br />
	 * @param imageDataList 단속이미지정보 목록
	 * @return 최초단속자료, 최종단속자료
	 */
	private DataObject[] getBgngDataAndEndData(List<DataObject> imageDataList) {
		DataObject[] bgngAndEnd = {null, null};

		if (!isEmpty(imageDataList.get(0).get("CRDN_TIMESTAMP"))) {
			DataObject bgngData = imageDataList.stream()
	            .min(Comparator.comparing(item -> new BigInteger(item.string("CRDN_TIMESTAMP"))))
	            .orElseThrow();

			DataObject endData = imageDataList.stream()
			    .max(Comparator.comparing(item -> new BigInteger(item.string("CRDN_TIMESTAMP"))))
		        .orElseThrow();

			bgngAndEnd[0] = bgngData;
			bgngAndEnd[1] = endData;
		}

		return bgngAndEnd;
	}

	/**최초단속정보와 최종단속정보를 기준으로 단속대장의 최초단속시간과 최종단속시간을 설정한다. <br />
	 * @param crdn 단속대장, bgngData 최초단속자료, endData 최종단속자료
	 * @return
	 */
	private void crdnTimeRangeSet(Crdn crdn, DataObject bgngData, DataObject endData) {
		if (isEmpty(bgngData) || isEmpty(endData)) return;

		String crdnBgngTm = endData.string("CRDN_BGNG_TM");
		if (!isEmpty(crdnBgngTm)) {
			crdn.setCrdnBgngTm(crdnBgngTm);
			crdn.setCrdnEndTm(endData.string("CRDN_END_TM"));
		} else {
			String crdnTm = endData.string("CRDN_TM");
			if (!isEmpty(crdnTm)){
				crdn.setCrdnBgngTm(bgngData.string("CRDN_TM"));
				crdn.setCrdnEndTm(crdnTm);
			}
		}
	}

	/**단속이미지정보를 List<FileInfo>로 변환한다. <br />
	 * @param imageList 단속이미지정보 목록, fileGroupType 연계파일그룹형식
	 * @return fileInfo리스트
	 */
	private List<FileInfo> parseDataInfoToFileInfo(List<DataObject> imageList, String fileGroupType){
		try {
			List<FileInfo> fileInfos = null;
			if ("JPG,TXT".contains(fileGroupType)) {
				List<File> files = imageList.stream()
					.map(imageInfo -> {
						File img = (File)imageInfo.get("file");
						return ifEmpty(img, new File(imageInfo.string("FILE_PATH")));
					})
					.toList();

				fileInfos = new FileInfoFactory().createFileInfos(null, files);
				for (int i = 0; i < fileInfos.size(); i++) {
					FileInfo fileInfo = fileInfos.get(i);
					fileInfo.setName("image" + i + "." + fileInfo.getExtension());
				}
			} else if ("BIN".equals(fileGroupType)) {
				fileInfos = new ArrayList<>();
				for (int i = 0; i < imageList.size(); i++){
					String fileName = imageList.get(i).string("B64IMAGE_NM");
					String b64str = imageList.get(i).string("B64IMAGE");
					FileInfo FileInfo = SingleFileParser.base64ToFileInfo(fileName, b64str, (i + 1));
					fileInfos.add(FileInfo);
				}
			}
			return fileInfos;
		} catch (Exception e) {
			throw runtimeException(e);
		}
	};
}