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

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import cokr.xit.base.user.service.bean.UserBean;
import cokr.xit.fims.FimsConf;
import cokr.xit.fims.crdn.Crdn;
import cokr.xit.fims.crdn.dao.CrdnUpdtMapper;
import cokr.xit.fims.levy.Levy;
import cokr.xit.fims.levy.LevyQuery;
import cokr.xit.fims.levy.dao.LevyMapper;
import cokr.xit.fims.mngt.Vltn;
import cokr.xit.fims.nxrp.NisIndivQuery;
import cokr.xit.fims.nxrp.dao.NisA01Mapper;
import cokr.xit.fims.sndb.SndbQuery;
import cokr.xit.fims.sndb.SndngDtl;
import cokr.xit.fims.sndb.dao.SndngDtlMapper;
import cokr.xit.foundation.Downloadable;
import cokr.xit.foundation.UserInfo;
import cokr.xit.foundation.component.AbstractBean;
import cokr.xit.foundation.data.DataObject;
import cokr.xit.interfaces.sntris.buga.Bu04BugaWSDTO;
import cokr.xit.interfaces.sntris.fileoffer.Ye22InputDTO;
import cokr.xit.interfaces.sntris.fileoffer.Ye22NoticeInfoDTO;
import cokr.xit.interfaces.sntris.prenotice.Bu18WebPreNoticeDTO;
import cokr.xit.interfaces.sntris.prenotice.Bu18WebReturnInfoDTO;
import cokr.xit.interfaces.sntris.service.bean.SntrisBean;

/**과태료 대장 단속 정보 등록 정보 관리 Bean
 *
 * <p>상세 설명:
 *
 * <pre>
 * ============ 변경 이력 ============
 * 2024-02-16	JoJH 최초 작성
 * ================================
 * </pre>
 */
@Component("snisBean")
public class SnisBean extends AbstractBean {
	@Resource(name = "sntrisBean")
	private SntrisBean sntrisBean;
	/** 발송 상세 정보 DAO */
	@Resource(name = "sndngDtlMapper")
	private SndngDtlMapper sndngDtlMapper;
	/** 과태료 대장 단속 정보 등록 정보 DAO */
	@Resource(name = "nisA01Mapper")
	private NisA01Mapper nisA01Mapper;
	/** 단속 대장 수정 정보 DAO */
	@Resource(name = "crdnUpdtMapper")
	private CrdnUpdtMapper crdnUpdtMapper;
	/** 사용자 정보 DAO */
	@Resource(name="userBean")
	private UserBean userBean;
	@Resource(name = "levyMapper")
	private LevyMapper levyMapper;

	/**발송대상 사전통보를 서울 세외수입 시스템의 과태료대장에 등록한다.
	 * @param req 발송대상 사전통보 조회조건
	 */
	public String registerPreNotices(SndbQuery req) {
		// 발송대상 사전통보 과태료대장 등록
		Map<String, Bu18WebPreNoticeDTO> prenotices = getPreNotices(req); // 과태료대장 등록을 위한 사전통보 추출
		Map<String, Bu18WebReturnInfoDTO> results = sntrisBean.registerPreNotices(prenotices); // 발송대상 ID별 과태료 등록 응답
		return update(results);
	}

	public String update(Map<String, Bu18WebReturnInfoDTO> results) {
		// 로컬 발송대상 상세 업데이트
		Set<String> ids = results.keySet(); // 발송대상 상세ID
		SndbQuery req = new SndbQuery().setSndngDtlIds(ids.toArray(new String[ids.size()]));
		List<SndngDtl> details = sndngDtlMapper.selectSndngDetails(req).stream()
			.map(detail -> update(
				results.get(
					detail.getSndngDtlId()),
					detail
				)
			)
			.filter(detail -> detail != null)
			.toList();
		sndngDtlMapper.updateSndngDtls(details);

		// 부과정보 업데이트
		Map<String, Bu18WebReturnInfoDTO> byCrdnIds = details.stream().collect(Collectors.toMap( // 단속 ID별 과태료 등록 응답
				detail -> detail.getCrdnId(),
				detail -> {
					String sndngDtlId = detail.getSndngDtlId();
					return results.get(sndngDtlId);
				}
			));
		ids = details.stream()  // 발송대상의 단속 ID
			.map(SndngDtl::getCrdnId)
			.collect(Collectors.toSet());
		List<Levy> levies = levyMapper.selectLevys(new LevyQuery().setCrdnIds(ids.toArray(new String[ids.size()]))).stream() // 업데이트를 위한 부과정보 가져오기
			.map(levy -> {
				Bu18WebReturnInfoDTO returned = byCrdnIds.get(levy.getCrdnId());
				return update(returned, levy);
			})
			.filter(levy -> levy != null)
			.toList();
		levyMapper.updateLevies(levies);

		// 단속정보 업데이트
		byCrdnIds.forEach((crdnId, returned) -> {
			Crdn crdn = new Crdn();
			crdn.setCrdnId(crdnId);
			crdn.setNxrpAcbKey(returned.getNoticeAk());

			crdnUpdtMapper.updateNxrpCrdnLinkYn(crdn); // 연계상태
			sndngDtlMapper.updateCrdnNxrpAcbKey(crdn); // 과태료대장키
		});

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

	private SndngDtl update(Bu18WebReturnInfoDTO returned, SndngDtl sndngDtl) {
		if (returned == null) return null;

		sndngDtl.setLinkMngKey(returned.getNoticeAk());             // 연계 키 <- 세외수입 과태료대장키
//		sndngDtl.setLinkMngKey(returned.getBuAfk());             // 연계 키 <- 세외수입 부과키
		sndngDtl.setEpayno(returned.getEnapbuNo());                 // 전자납부번호
		setBankAccounts(returned.accountGetters(), sndngDtl.bankAccountSetters()); // 은행 / 계좌번호
		sndngDtl.setSndngDtlSttsCd("01");

		return sndngDtl;
	}

	private void setBankAccounts(Map<String, Supplier<String>> getters, List<BiConsumer<String, String>> setters) {
		getters.forEach((bank, account) -> {
			if (setters.isEmpty()) return;

			setters.remove(0).accept(bank, account.get().replace("-", ""));
		});
	}

	private Levy update(Bu18WebReturnInfoDTO returned, Levy levy) {
		if (returned == null) return null;

		levy.setNxrpLevyKey(returned.getBuAfk());    // 세외수입부과 키 <- 세외수입시스템 일련번호
		levy.setEpayno(returned.getEnapbuNo());      // 전자납부번호
		setBankAccounts(returned.accountGetters(), levy.bankAccountSetters()); // 은행 / 계좌번호

		return levy;
	}

	/**발송대상 상세ID별 사전통지를 반환한다.
	 * @param req 발송대상 사전통지 조회조건
	 * @return 발송대상 ID별 사전통지
	 */
	private Map<String, Bu18WebPreNoticeDTO> getPreNotices(SndbQuery req) {
		// 발송대상 ID 추출
		List<DataObject> targets = sndngDtlMapper.selectSndngTrgtDtlIds(req).stream()
			.filter(row -> "00".equals(row.get("SNDNG_DTL_STTS_CD")))
			.toList();
		if (targets.isEmpty()) return Collections.emptyMap();

		List<String> crdnIds = targets.stream()
			.map(target -> target.string("CRDN_ID"))
			.toList();
		Map<String, DataObject> crdnMap = nisA01Mapper.selectCrdnList(crdnIds).stream().collect(
			Collectors.toMap(info -> info.string("CRDN_ID"), info -> info)
		);
		// 발송대상 ID별 단속정보 병합
		targets.forEach(target -> {
			DataObject found = crdnMap.get(target.string("CRDN_ID"));
			if (found == null) return;

			target.putAll(found);
		});

		// 발송대상 ID별 사전통지 생성
		String now = dateFormats.now();
		UserInfo currentUser = currentUser();
		DataObject userInfo = userBean.getUserInfo(currentUser.getId());
		return targets.stream().collect(Collectors.toMap(
			target -> target.string("SNDNG_DTL_ID"),
			target -> createPreNotice(currentUser, now, userInfo, target)
		));
	}

	public Downloadable downloadPreNotices(SndbQuery req) {
		Map<String, Bu18WebPreNoticeDTO> prenotices = getPreNotices(req);
		return sntrisBean.getDownloadable(prenotices.values().stream().toList());
	}

	private Bu18WebPreNoticeDTO createPreNotice(UserInfo currentUser, String now, DataObject userInfo, DataObject crdnInfo) {
		Bu18WebPreNoticeDTO prenotice = new Bu18WebPreNoticeDTO();

		prenotice.setBigo(crdnInfo.string("ETC_CN"));                // 비고
		prenotice.setBookNo(crdnInfo.string("SNDNG_DTL_ID"));        // 자료키 <- 발송상세 아이디
		prenotice.setSidoCd("11");                                   // 시도코드
		prenotice.setSiguCd(currentUser.info("instCd"));             // 시구코드 <- 기관코드
//		prenotice.setBuseoCd(currentUser.getDeptCode());             // 서울 세외수입 시스템에서 시도, 시구, 작업자ID로 조회해서 설정

		prenotice.setDisposalContent("부과");                          // 처분내용
		prenotice.setRawBasis(Vltn.legalBasis(crdnInfo));            // 부과근거
		prenotice.setSemokCd(crdnInfo.string("ACNTG_SE_CD") + crdnInfo.string("TXITM_CD")); // 세목코드
		long taxAmt = toLong(crdnInfo.get("FFNLG_AMT"));
		prenotice.setTaxAmt(taxAmt);                                 // 과세금액
		prenotice.setPreTaxAmt(toLong(taxAmt * 0.8));                //

		prenotice.setJukbalDtlAddr(Crdn.location(crdnInfo));         // 위반장소
		prenotice.setJukbalMgrNm(currentUser.getName());             // 적발담당자
		prenotice.setJukbalTarget(String.format("%s(%s)", crdnInfo.string("VLTN_ARTCL"), crdnInfo.string("VHRNO"))); // 적발내용
		prenotice.setJukbalTelNo(userInfo.string("TELNO"));          // 적발담당자 전화번호
		String crdnYmdhms = crdnInfo.string("CRDN_YMD") + crdnInfo.string("CRDN_TM");
		prenotice.setJukbalYm(crdnYmdhms.substring(0, 6));           // 적발년월
		prenotice.setJukbalYmdHs(crdnYmdhms.substring(0, 12));       // 위반일시

		prenotice.setNapId(blankIfEmpty(crdnInfo.string("RTPYR_NO")).replace("-", ""));// 납세자 주민번호
		prenotice.setNapNm(crdnInfo.string("RTPYR_NM"));             // 납세자 이름
		prenotice.setNapMobilNo(crdnInfo.string("RTPYR_MBL_TELNO")); // 납세자 휴대폰 번호
		prenotice.setNapTelNo(crdnInfo.string("RTPYR_TELNO"));       // 납세자 전화번호

		prenotice.setNapDzipAddr(crdnInfo.string("ADDR"));           // 도로명 우편주소
		prenotice.setNapDdtlAddr(crdnInfo.string("DTL_ADDR"));       // 도로명 상세주소
		prenotice.setNapDzipCd(crdnInfo.string("ZIP"));              // 도로명 우편번호
/*
		prenotice.setNapDrefAddr("");	// 도로명 참고항목

		prenotice.setNapZipAddr("");	// 지번 우편주소
		prenotice.setNapDtlAddr("");	// 지번 상세주소
		prenotice.setNapZipCd("");	// 지번 우편번호
*/
		prenotice.setSendYmd(crdnInfo.string("ADVNTCE_BGNG_YMD"));   // 사전통지일
		prenotice.setSubmitYmd(crdnInfo.string("ADVNTCE_DUDT_YMD")); // 의견제출기한

		prenotice.setSysGubun(FimsConf.get().getSystemGubun());      // 시스템 구분
		prenotice.setLastWorkId(notEmpty(userInfo.string("NTRIS_KEY"), "세외수입 인사대체키")); // 작업자 ID <- 세외수입 사용자키
		prenotice.setLastWorkNm(currentUser.getName());              // 작업자 이름
		prenotice.setWorkYmd(now);                                   // 작업일자

		return prenotice;
	}

	/**주어진 조건으로 서울 세외수입에 연계하여 과태료대장을 조회한다.
	 * @param input 조회조건
	 * @return 과태료대장 조회결과
	 */
	public List<Ye22NoticeInfoDTO> getPreNoticeList(SndngDtl sndngDtl) {
		Ye22InputDTO input = new Ye22InputDTO();
		String buAk = sndngDtl.getLinkMngKey(); // 연계관리키 <- 세외수입 과태료대장키
		input.setBuAk(buAk);
		input.setSystemGubun(FimsConf.get().getSystemGubun());
		List<Ye22NoticeInfoDTO> prenotices = sntrisBean.getPreNoticeList(input);
		update(sndngDtl, prenotices);
		return prenotices;
	}

	private void update(SndngDtl sndngDtl, List<Ye22NoticeInfoDTO> notices) {
		if (isEmpty(notices)) return;

		Map<String, Ye22NoticeInfoDTO> noticeMap = notices.stream().collect(Collectors.toMap(
			notice -> Long.toString(notice.getBuAk()),
			notice -> notice
		));
		List<String> nxrpLevyKeys = noticeMap.keySet().stream().toList();
		List<Levy> levies = levyMapper.selectLevys(new LevyQuery().setNxrpLevyKeys(nxrpLevyKeys));
		levies.forEach(levy -> {
			Ye22NoticeInfoDTO notice = noticeMap.get(levy.getNxrpLevyKey());
			if (notice == null) return;

			levy.setSggCd(notice.getSiguCd());            // 시군구 코드
			levy.setDeptCd(notice.getBuseoCd());          // 부서코드

			String semokCd = notice.getSemokCd();
			if (!semokCd.contains(levy.getTxitmCd()))
				levy.setTxitmCd(semokCd);                 // 세목코드
			levy.setLevyNo(notice.getTaxNo());            // 부과번호
			levy.setLevyYmd(notice.getTaxYmd());          // 부과일자
			levy.setDudtYmd(notice.getNapgiYmd());        // 납기일자
			levy.setRcvmtYmd(notice.getNapbuYmd());       // 수납일자

			levy.setLevyPcptax((int)notice.getTaxAmt());  // 부과본세 <- 과세금액
			levy.setLevyAdamt((int)notice.gasanAmt());    // 부과가산금
			levy.setLastAmt((int)notice.totalAmt());      // 최종금액
			levy.setDudtAftrAmt(null);

			levy.setDudtAftrYmd(notice.getNapgiAftYmd()); // 납기후일자
			levy.setTxtnThing(notice.getMulNm());         // 물건이름

			levy.setSzrYmd(notice.getApYmd());            // 압류일자
			levy.setEpayno(notice.getEnapbuNo());         // 전자납부번호
//			levy.setNxrpLevyKey(Long.toString(notice.getBuAk())); // 세외수입 부과키

			setBankAccounts(notice.accountGetters(), levy.bankAccountSetters()); // 은행 / 계좌번호
		});
		int affected = levyMapper.updateLevies(levies);

		List<SndngDtl> details = sndngDtlMapper.selectSndngDetails(new SndbQuery().setSndngDtlIds(sndngDtl.getSndngDtlId()));
		details.forEach(detail -> {
			Ye22NoticeInfoDTO notice = noticeMap.get(detail.getLinkMngKey());
			if (notice == null) return;

			detail.setSggCd(notice.getSiguCd());            // 시군구 코드
			detail.setTaxNo(notice.getNapseNo());           // 납세번호
			detail.setEpayno(notice.getEnapbuNo());         // 전자납부번호
			detail.setVhrno(notice.getMulNm());             // 차량번호

//			detail.setLevyNo(notice.getTaxNo());            // 부과번호
			detail.setLevyYmd(notice.getTaxYmd());          // 부과일자

			detail.setDudtYmd(notice.getNapgiYmd());        // 납기일자
			detail.setDudtAftrYmd(notice.getNapgiAftYmd()); // 납기후일자

			detail.setPcptax((int)notice.getTaxAmt());      // 본세
			detail.setAdamt((int)notice.gasanAmt());        // 가산금
			detail.setLastAmt((int)notice.totalAmt());      // 최종금액
			detail.setDudtAftrAmt(null);                    // 납기후 금액

			setBankAccounts(notice.accountGetters(), detail.bankAccountSetters()); // 은행 / 계좌번호
			if (toInt(detail.getSndngDtlSttsCd()) < 2)
        		detail.setSndngDtlSttsCd("02");				// 발송 상태 코드(FIM050) 02:가상계좌취득
		});
		for (SndngDtl detail: details) {
			affected += sndngDtlMapper.updateSndngDtl(detail);
		}
	}

	/**과태료 대장 단속 정보 등록 정보를 등록한다.
	 * @param nisIndivA01 과태료 대장 단속 정보 등록
	 * @return 저장 여부
	 * <ul><li>저장됐으면 true</li>
	 *     <li>그렇지 않으면 false</li>
	 * </ul>
	 */
	public String createBu04Buga(NisIndivQuery req) {
		String userId = currentUser().getId();
		DataObject userInfo = userBean.getUserInfo(userId); // 1. 사용자 정보
		String userDept = userInfo.string("DEPT_CD");
		DataObject crdnInfo = nisA01Mapper.selectCrdnInfo(req.getCrdnId()); // 3. 세외수입 과태료대장에 연계할 단속 정보
		String
			sggCd = crdnInfo.string("SGG_CD"),
			crdnId = crdnInfo.string("CRDN_ID"),
			vhrno = crdnInfo.string("VHRNO"),
			sndngYmd = req.getSndngYmd(),
			taskSeCd = crdnInfo.string("TASK_SE_CD"); // 업무 구분 코드

		Bu04BugaWSDTO buga = new Bu04BugaWSDTO();
		buga.setTaxGubun("3"); //1: 정기, 2: 수시, 3: 신고
		buga.setTaxNo("");
		buga.setTaxYm(sndngYmd.substring(0, 6));
		buga.setTaxYmd(sndngYmd);

		buga.setBigo(crdnInfo.string("ETC_CN"));
		buga.setBookNo(crdnId);
		buga.setBunhalCnt(0);
		buga.setBuseoCd(userDept);
		buga.setEtc1("");
		buga.setGasanAmtSkipGubun("");
		buga.setGasanRateGubun("");
		buga.setGigum(0);
		buga.setGigumGasanAmt(0);
		buga.setGigumIja(0);
		buga.setGukse(0);
		buga.setGukseGasanAmt(0);
		buga.setGukseIja(0);
		buga.setGuse(0);
		buga.setGuseGasanAmt(0);
		buga.setGuseIja(0);

		if ("PES".equals(taskSeCd)) {
			buga.setHangmok1("차고지 외 밤샘주차");
			buga.setHangmok2("위반일시 : " + crdnInfo.string("CRDN_YMD_TM_MASK"));
			buga.setHangmok3("위반장소 : " + crdnInfo.string("CRDN_PLC"));
			buga.setHangmok4("화물자동차운수사업법");

			switch (blankIfEmpty(crdnInfo.string("CRDN_INPT_SE_CD"))) {
			case "06": buga.setHangmok5("관내단속"); break;
			case "08": buga.setHangmok5(crdnInfo.string("ETC_CN")); break;
			default: buga.setHangmok5(""); break;
			}
		} else if ("DPV,ECA".contains(taskSeCd)) {
			buga.setHangmok1("위반일시 : " + crdnInfo.string("CRDN_YMD_TM_MASK"));		// 관리항목1
			buga.setHangmok2("");													// 관리항목2
			buga.setHangmok3("위반장소 : " + crdnInfo.string("CRDN_PLC"));				// 관리항목3
			buga.setHangmok4("");													// 관리항목4
			buga.setHangmok5("위반항목 : " + crdnInfo.string("VLTN_ARTCL"));			// 관리항목5
		} else
			throw new RuntimeException("지정되지 않은 업무 구분코드가 입력되었습니다.");		// 예외를 발생시켜서 DB Rollback
		buga.setHangmok6(sggCd + crdnId);	// 관리6항목

		buga.setHdongCd(crdnInfo.string("DONG_CD"));
		buga.setLastWorkDate(sndngYmd);
		buga.setLastWorkId(userId);

		buga.setMulGubun("02"); // 차량
		buga.setMulNm(vhrno);
		buga.setMulSpcCd("9"); // 무번지
/*
		buga.setMulAptNm("");
		buga.setMulBan("");
		buga.setMulBdongriCd("");
		buga.setMulBon("");
		buga.setMulBu("");
		buga.setMulDong("");
		buga.setMulDtlAddr("");
		buga.setMulHosu("");
		buga.setMulOcrSiguCd("");
		buga.setMulTong("");
		buga.setMulZipAddr("");
		buga.setMulZipCd("");
*/
		String pyrSeCd = Map.of(
				"01", "01", // 주민(01)  -> 개인(01)
				"02", "12", // 외국인(02) -> 외국인(12)
				"03", "30", // 법인(03)  -> 법인(30)
				"04", "32"  // 사업자(04) -> 사업자(32)
			).getOrDefault(crdnInfo.string("RTPYR_SE_CD"), "99"); // 기타(05) -> 기타(99)
		buga.setNapGubun(pyrSeCd);
		String rtpyrNo = crdnInfo.string("RTPYR_NO");
		buga.setNapId(rtpyrNo);
		String rtpyrNm = crdnInfo.string("RTPYR_NM");
		buga.setNapNm(rtpyrNm);
		buga.setNapMobilNo(crdnInfo.string("RTPYR_MBL_TELNO"));
		buga.setNapTelNo(crdnInfo.string("RTPYR_TELNO"));
		buga.setNapEmail(crdnInfo.string("RTPYR_EML"));

		buga.setNapBldBon(crdnInfo.string("BMNO"));
		buga.setNapBldBu(crdnInfo.string("BSNO"));

		buga.setNapDzipCd(crdnInfo.string("ZIP"));
		buga.setNapDzipAddr(crdnInfo.string("ADDR"));
		buga.setNapDdtlAddr(crdnInfo.string("DTL_ADDR"));

		buga.setNapDoroAddr("");
		buga.setNapDoroCd("");
		buga.setNapDrefAddr("");
		buga.setNapDtlAddr("");

		buga.setNapUndYn("1".equals(crdnInfo.string("UDGD_SE_CD")) ? "Y" : "N");
		buga.setNapZipAddr("");
		buga.setNapZipCd(crdnInfo.string("ZIP"));

		buga.setOcrBuseoCd("");
		buga.setOcrSiguCd("");
		buga.setResideStatus("");
		buga.setRowStatus("");
		buga.setSemokCd(crdnInfo.string("TXITM_CD"));
		buga.setSendYmd(sndngYmd);
		buga.setSidoCd("11"); // 고정
		buga.setSiguCd(sggCd);
		buga.setSise(0);
		buga.setSiseGasanAmt(0);
		buga.setSiseIja(0);
		buga.setSokwanBuseoCd("");
		buga.setSokwanMgrNm("");
		buga.setSokwanTelNo("");
		buga.setSpecialRate(0);
		buga.setSpecialRateApplyCnt(0);
		buga.setSpecialRateApplySayu("");
		buga.setSpecialRateHighRate(0);
		buga.setSuBuseoCd("");
		buga.setSysGubun("");

		buga.setTaxAmt(0);
		buga.setVatAmt(0);
		buga.setNapbuYmd("");
		buga.setNapgiAftYmd("");
		buga.setNapgiYmd("");

		try {
			sntrisBean.register(userId, buga);
			return "";
		} catch (Exception e) {
			throw runtimeException(e);
		}
	}
}