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

import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import cokr.xit.base.file.FileInfo;
import cokr.xit.base.file.service.bean.FileBean;
import cokr.xit.fims.cmmn.CrdnPayerAddrHstry;
import cokr.xit.fims.cmmn.CrdnSttsHstry;
import cokr.xit.fims.cmmn.service.bean.CrdnPayerAddrHstryBean;
import cokr.xit.fims.cmmn.service.bean.CrdnSttsHstryBean;
import cokr.xit.fims.crdn.Crdn;
import cokr.xit.fims.crdn.Team;
import cokr.xit.fims.crdn.dao.CrdnInfoMapper;
import cokr.xit.fims.crdn.dao.CrdnInstMapper;
import cokr.xit.fims.crdn.dao.CrdnTeamMapper;
import cokr.xit.fims.crdn.dao.CrdnUpdtMapper;
import cokr.xit.fims.excl.LevyExcl;
import cokr.xit.fims.excl.service.bean.LevyExclBean;
import cokr.xit.fims.mngt.service.bean.TaskProcessor;
import cokr.xit.foundation.UserInfo;
import cokr.xit.foundation.component.AbstractBean;
import cokr.xit.foundation.data.DataFormat;
import cokr.xit.foundation.data.DataObject;
import cokr.xit.foundation.data.DataProc;

/** 단속 관리 Bean
 * @author leebj
 */
@Component("crdnBean")
public class CrdnBean extends AbstractBean {
	@Resource(name = "crdnSttsHstryBean")
	private CrdnSttsHstryBean crdnSttsHstryBean;
	@Resource(name = "crdnPayerAddrHstryBean")
	private CrdnPayerAddrHstryBean crdnPayerAddrHstryBean;
	@Resource(name = "fileBean")
	private FileBean fileBean;
	@Resource(name = "crdnStngBean")
	private CrdnStngBean crdnStngBean;
	@Resource(name = "crdnInfoMapper")
	private CrdnInfoMapper crdnInfoMapper;
	@Resource(name = "crdnInstMapper")
	private CrdnInstMapper crdnInstMapper;
	@Resource(name = "crdnUpdtMapper")
	private CrdnUpdtMapper crdnUpdtMapper;
	@Resource(name = "crdnTeamMapper")
	private CrdnTeamMapper crdnTeamMapper;
	@Resource(name = "levyExclBean")
	protected LevyExclBean levyExclBean;

	public List<Crdn> getCrdns(List<String> crdnIds) {
		return crdnInfoMapper.selectCrdns(crdnIds);
	}

	public Crdn getCrdn(String crdnId) {
		return crdnInfoMapper.selectCrdn(crdnId);
	}

	public List<DataProc> create(List<Crdn> crdns, boolean exclude) {
		TaskProcessor taskProcessor = TaskProcessor.get();
		List<Crdn> duplicates = taskProcessor.getDuplicates(crdns), // 중복 단속목록
				   distincts = crdns.stream().filter(crdn -> !duplicates.contains(crdn)).toList(); // 중복되지 않는 단속목록
		ArrayList<DataProc> result = new ArrayList<>();

		for (Crdn crdn: duplicates) {
			result.add(
				new DataProc()
					.setTarget(crdn)
					.setSuccess(false)
					.setInfo("reason", "duplicate")
			);
		}
		if (distincts.isEmpty())
			return result;

		Map<Crdn, LevyExcl> levyExcls = exclude ? taskProcessor.getExcluded(distincts) : Collections.emptyMap();

		for (Crdn crdn: distincts) {
			List<FileInfo> attachments = crdn.getAttachments();
			crdn.setAtchFileCnt(isEmpty(attachments) ? 0 : attachments.size());
			//단속 대장 등록
			createLedger(crdn);

			LevyExcl excl01 = levyExcls.get(crdn);
			if (excl01 != null) {
				excl01.setCrdnId(crdn.getCrdnId());
				levyExclBean.createLevyExcl(excl01);
			}

			//단속납부자이력(TB_CRDN_PAYER_HSTRY) 대장 등록
			if (!isEmpty(crdn.getRtpyrId())) {
	            CrdnPayerAddrHstry crdnPayerAddrHstry = new CrdnPayerAddrHstry();
	            crdnPayerAddrHstry.setCrdnId(crdn.getCrdnId());
	            crdnPayerAddrHstry.setRtpyrId(crdn.getRtpyrId());
	            crdnPayerAddrHstry.setAddrSn(crdn.getAddrSn());

		    	// 단속 대장의 납부자ID 수정 및 단속 납부자 이력(TB_CRDN_PAYER_HSTRY) 대장에 등록한다.
	            crdnPayerAddrHstryBean.createCrdnPayerAddrHstry(crdnPayerAddrHstry);
			}

			//파일 등록
			if (!isEmpty(attachments)) {
				attachments.forEach(fileInfo -> fileInfo.setInfoType(Crdn.INF_TYPE).setInfoKey(crdn.getCrdnId()));
				fileBean.create(attachments);
			}

			DataProc dataProc = new DataProc().setTarget(crdn);
			String ersrRegYmd = crdn.getErsrRegYmd();
			if (!isEmpty(ersrRegYmd)) // 말소차량
				dataProc.setInfo(
					"erased",
					new DataObject()
						.set("vhrno", crdn.getVhrno())
						.set("date", DataFormat.yyyy_mm_dd(ersrRegYmd))
				);
			result.add(dataProc);
		}

		return result;
	}

	public boolean create(Map<String, Object> nonQueryRequest, Crdn crdn, List<FileInfo> fileInfoList) {
		int fileCount = isEmpty(fileInfoList) ? 0 : fileInfoList.size();
		crdn.setAtchFileCnt(fileCount);

		//단속 대장 등록
		boolean result = createLedger(crdn);
		if (!result)
			throw new RuntimeException("중복된 단속자료입니다.");

		LevyExcl excl01 = TaskProcessor.get().getLevyExcl(crdn);
		if (excl01 != null) {
			String rtnScs = levyExclBean.createLevyExcl(excl01);
			if (rtnScs.contains("[F]"))
				throw new RuntimeException("단속자료 등록 중 부과제외 자료 등록에 실패하였습니다.");
		}
		if (!result)
			throw new RuntimeException("단속자료 등록 중 단속상태 이력 등록에 실패하였습니다.");

		//단속납부자이력(TB_CRDN_PAYER_HSTRY) 대장 등록
		if (!isEmpty(crdn.getRtpyrId())) {
            CrdnPayerAddrHstry crdnPayerAddrHstry = new CrdnPayerAddrHstry();
            crdnPayerAddrHstry.setCrdnId(crdn.getCrdnId());
            crdnPayerAddrHstry.setRtpyrId(crdn.getRtpyrId());
            crdnPayerAddrHstry.setAddrSn(crdn.getAddrSn());

	    	// 단속 대장의 납부자ID 수정 및 단속 납부자 이력(TB_CRDN_PAYER_HSTRY) 대장에 등록한다.
            int affected = crdnPayerAddrHstryBean.createCrdnPayerAddrHstry(crdnPayerAddrHstry);
            if (affected != 1)
				throw new RuntimeException("단속자료 등록 중 단속 납부자주소 이력 등록에 실패하였습니다.");
		}

		//파일 등록
		if (fileCount > 0) {
			fileInfoList.forEach(fileInfo -> fileInfo.setInfoType(Crdn.INF_TYPE).setInfoKey(crdn.getCrdnId()));
			int affected = fileBean.create(fileInfoList);
			if (affected == 0)
				throw new RuntimeException("단속자료 등록 중 파일 등록에 실패하였습니다.");
		}

		return result;
	}

	private void setAmount(Crdn crdn) {
		int[] basicAmt = crdnStngBean.getBasicAmt(crdn);
		crdn.setFfnlgCrdnAmt(basicAmt[0]);
		crdn.setFfnlgAmt(basicAmt[0]);
		crdn.setAdvntceAmt(basicAmt[1]);
	}

	/**단속 대장 정보를 등록한다.
	 * @param crdn 단속 대장
	 * @return 저장 여부
	 * <ul><li>저장됐으면 true</li>
	 *     <li>그렇지 않으면 false</li>
	 * </ul>
	 */
	public boolean createLedger(Crdn crdn) {
		setAmount(crdn);
		//단속팀 정보 갱신
		if (!isEmpty(crdn.getTeamId())) {
			DataObject teamInfo = crdnTeamMapper.selectTeamInfo(crdn.getTeamId());
			String sggCd = crdn.getSggCd();
			String taskSeCd = crdn.getTaskSeCd();

			Team team = new Team();
			team.setTaskSeCd(taskSeCd);
			team.setSggCd(sggCd);
			team.setTeamNm(teamInfo.string("TEAM_NM"));
			team.setCrdnSeCd(crdn.getCrdnSeCd());
			team.setTeamer1(crdn.getTeamer1());
			team.setTeamer2(crdn.getTeamer2());
			team.setTeamer3(crdn.getTeamer3());
			team.setTeamer4(crdn.getTeamer4());

			crdnStngBean.saveTeamInfo(true, team);
		}

		String crdnSttsCd = null;
		if ("Y".equals(crdn.getCvlcptLinkYn())) { //민원연계자료일 경우
			crdnSttsCd = "01";
		} else { //민원연계자료가 아닐 경우
			crdnSttsCd = isEmpty(crdn.getRtpyrId()) ? "01" : "21"; // 01: 초기상태 21: 납부자등록완료
		}
		String orgStts = crdn.getCrdnSttsCd();
		crdn.setCrdnSttsCd(crdnSttsCd);
		crdn.setCrdnSttsChgDt(dateFormats.now());
		boolean saved = crdnInstMapper.insert(crdn);
		crdn.setCrdnSttsCd(orgStts);

		// 단속상태이력(TB_CRDN_STTS_HSTRY) 대장 등록
		String crdnId = crdn.getCrdnId();
		CrdnSttsHstry crdnSttsHstry = new CrdnSttsHstry();
		crdnSttsHstry.setCrdnId(crdnId);
		crdnSttsHstry.setBfrSttsCd("");
		crdnSttsHstry.setBfrSttsChgDt("");
		crdnSttsHstry.setCrdnSttsCd(crdnSttsCd);
		crdnSttsHstry.setTaskDtlId(crdnId);
		crdnSttsHstry.setEtcCn("");
		crdnSttsHstryBean.create(crdnSttsHstry);

		return saved;
	}

	/**단속 자료를 수정한다.
	 * @param crdn 단속
	 * @return 저장 여부
	 * <ul><li>저장됐으면 true</li>
	 *     <li>그렇지 않으면 false</li>
	 * </ul>
	 */
	public String updateCrackdown(Crdn crdn) {
		setAmount(crdn);

        // 단속(TB_CRDN) 대장을 수정한다.
        if (crdnUpdtMapper.updateEditCrdn(crdn) != 1)
            throw new RuntimeException("단속 대장 수정에 실패하였습니다.");

        // 단속 부가 정보(TB_CRDN_ADI) 대장을 수정한다.
        if (crdnUpdtMapper.updateCrdnAddition(crdn) != 1)
            throw new RuntimeException("단속 부가 정보 수정에 실패하였습니다.");

        return "[S] 작업이 정상 처리 되었습니다.";
	}

	public String updateCrdnPayer(Crdn crdn) {
		updateCrackdown(crdn);

        CrdnPayerAddrHstry crdnPayerAddrHstry = new CrdnPayerAddrHstry();
        crdnPayerAddrHstry.setCrdnId(crdn.getCrdnId());
        crdnPayerAddrHstry.setRtpyrId(crdn.getRtpyrId());
        crdnPayerAddrHstry.setAddrSn(crdn.getAddrSn());
		crdnPayerAddrHstryBean.updateCrdnPayerAddr(crdnPayerAddrHstry);

		return "[S] 작업이 정상 처리 되었습니다.";
	}

	public void create(LevyExcl excl) {
		levyExclBean.createLevyExcl(excl);
	}

	/**단속 자료를 삭제한다.
	 * @param crdn 단속
	 * @return 저장 여부
	 * <ul><li>저장됐으면 true</li>
	 *     <li>그렇지 않으면 false</li>
	 * </ul>
	 */
	public String removeCrackdown(Crdn crdn) {
		// 단속 ID로 단속 정보 조회
		DataObject infoCrdn = crdnInfoMapper.selectCrdnInfo(crdn.getCrdnId());

		// 단속상태이력(TB_CRDN_STTS_HSTRY)
		CrdnSttsHstry crdnSttsHstry = CrdnSttsHstry.create("99", null, null, infoCrdn);
		// 단속 상태 이력(TB_CRDN_STTS_HSTRY) 대장에 등록한다.
		boolean rtnScs = crdnSttsHstryBean.createHstryUpdateCrdnSttsCd(crdnSttsHstry);
		if (!rtnScs)
			throw new RuntimeException("단속 삭제 중 단속 대장의 단속상태코드 변경에 실패하였습니다.");

		Map<String, Object> paramMap = new HashMap<String, Object>();
		paramMap.put("crdnIds", List.of(crdn.getCrdnId()));
		paramMap.put("removedBy", UserInfo.current().getId());
		paramMap.put("delRsn", crdn.getDelRsn());

        // 단속(TB_CRDN) 대장을 삭제한다.
		int affected = crdnUpdtMapper.deleteCrdn(paramMap);
        if (affected != 1)
            throw new RuntimeException("단속 대장 삭제에 실패하였습니다.");

        // 단속(TB_CRDN) 대장을 삭제한다.
        affected = crdnUpdtMapper.deleteCrdnAdi(paramMap);
        if (affected != 1)
            throw new RuntimeException("단속 부가 정보 삭제에 실패하였습니다.");

        return "[S] 작업이 정상 처리 되었습니다.";
	}

	/**단속 자료의 납부자 ID를 삭제한다.
	 * @param crdn 단속
	 * @return 저장 여부
	 * <ul><li>저장됐으면 true</li>
	 *     <li>그렇지 않으면 false</li>
	 * </ul>
	 */
    public String removeCrackdownPayer(Crdn crdn) {
        DataObject crdnInfo = crdnInfoMapper.selectCrdnInfo(crdn.getCrdnId());
        if (crdnInfo == null)
            return "[F] 작업중 단속 자료가 조회되지 않습니다.";
        if (!"21,22".contains(crdnInfo.string("CRDN_STTS_CD")))
            return  "[F] 작업은 단속 처리상태가 납부자등록, 사전부과대상 자료만 가능합니다.";

        if (crdnUpdtMapper.deleteCrdnPayer(crdn) != 1)
            throw new RuntimeException("단속 대장에 납부자 삭제에 실패하였습니다.");

        // 단속 상태 코드 변경
        CrdnSttsHstry crdnSttsHstry = CrdnSttsHstry.create("01", null, crdn.getEtcCn(), crdnInfo);

        // 단속 상태 이력(TB_CRDN_STTS_HSTRY) 대장에 등록한다.
        if (!crdnSttsHstryBean.createHstryUpdateCrdnSttsCd(crdnSttsHstry))
            throw new RuntimeException("단속 대장의 단속상태코드 변경에 실패하였습니다.");

        return "[S] 작업이 정상 처리 되었습니다.";
    }

	/**단속 자료의 처리 상태 코드를 수정한다.
	 * @param crdn 단속
	 * @return 저장 여부
	 * <ul><li>저장됐으면 true</li>
	 *     <li>그렇지 않으면 false</li>
	 * </ul>
	 */
	public String updateCrackdownStatus(Crdn crdn) {
		crdnUpdtMapper.updateCrdnStatus(crdn);

		// 단속 ID로 단속 정보 조회
		DataObject crdnInfo = crdnInfoMapper.selectCrdnInfo(crdn.getCrdnId());

        // 단속상태이력(TB_CRDN_STTS_HSTRY)
		CrdnSttsHstry crdnSttsHstry = CrdnSttsHstry.create(crdn.getCrdnSttsCd(), null, "[개별총정보-단속상태변경] " + crdn.getEtcCn(), crdnInfo);

		// 단속 상태 이력(TB_CRDN_STTS_HSTRY) 대장에 등록한다.
		if (!crdnSttsHstryBean.createHstryUpdateCrdnSttsCd(crdnSttsHstry))
			throw new RuntimeException("단속 대장의 단속상태코드 변경에 실패하였습니다.");

        return "[S] 작업이 정상 처리 되었습니다.";
	}

	/**단속 정보를 삭제한다.
	 * @param crdnIds 단속아이디 목록
	 * @return 저장 여부
	 * <ul><li>저장됐으면 true</li>
	 *     <li>그렇지 않으면 false</li>
	 * </ul>
	 */
	public boolean remove(String... crdnIds) {
		return !isEmpty(crdnIds)
			&& crdnUpdtMapper.deleteCrdn(new DataObject()
				.set("crdnIds", crdnIds)
				.set("removedBy", UserInfo.current().getId())
			) >= 1;
	}

	/** 장비 연계파일을 삭제한다.
	 * @param workPath 작업 디렉토리, fileName 파일명
	 * @return 저장 여부
	 */
	public boolean removeEquipmentLinkFile(String workPath, String fileName) {
		try {
			File file = Paths.get(workPath, fileName).toFile();
			return file.exists() && file.delete();
		} catch (Exception e) {
			throw runtimeException(e);
		}
	}
}