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

import java.util.List;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import cokr.xit.fims.cmmn.CmmnUtil;
import cokr.xit.fims.cmmn.CrdnSttsHstry;
import cokr.xit.fims.cmmn.service.bean.CrdnSttsHstryBean;
import cokr.xit.fims.levy.Levy;
import cokr.xit.fims.levy.LevyQuery;
import cokr.xit.fims.levy.dao.LevyMapper;
import cokr.xit.fims.rcvm.Rcvmt;
import cokr.xit.fims.rcvm.RcvmtQuery;
import cokr.xit.fims.rcvm.dao.RcvmtMapper;
import cokr.xit.foundation.component.AbstractBean;
import cokr.xit.foundation.data.DataObject;

/**수납 대장 정보 관리 Bean
 *
 * <p>상세 설명:
 *
 * <pre>
 * ============ 변경 이력 ============
 * 2023-08-31    JoJH 최초 작성
 * ================================
 * </pre>
 */
@Component("rcvmtBean")
public class RcvmtBean extends AbstractBean {

    /** 수납 대장 정보 DAO */
    @Resource(name = "rcvmtMapper")
    private RcvmtMapper rcvmtMapper;

    /** 부과 대장 정보 DAO */
    @Resource(name = "levyMapper")
    private LevyMapper levyMapper;

    /** 단속 상태 이력 정보 Bean */
    @Resource(name = "crdnSttsHstryBean")
    private CrdnSttsHstryBean crdnSttsHstryBean;

    /**지정한 조건에 따라 수납 대장 목록을 조회하여 반환한다.
     * @param req 수납 대장 조회 조건
     * @return 수납 대장 목록
     */
    public List<DataObject> getRcvmtList(RcvmtQuery req) {
        if (req.getDelYn() == null) {
            req.setDelYn("N");
        }
        // 정렬 확인
        if (req.getOrderBy() == null) {
            if (req.getBy() == null) {
                req.setOrderBy("REG_DT");
            } else {
                req.setOrderBy(CmmnUtil.convertCamelCaseToSnakeCase(req.getBy()));
            }
        }

        return rcvmtMapper.selectRcvmtList(req);
    }

    /**지정한 조건에 따라 수납 대장 객체들을 반환한다.
     * @param req 수납 대장 조회 조건
     * @return 수납 대장 객체 목록
     */
    public List<DataObject> getRcvmts(RcvmtQuery req) {
        if (req.getDelYn() == null) {
            req.setDelYn("N");
        }
        if (req.getOrderBy() == null) {
            req.setOrderBy("R.RCVMT_ID DESC");
        }

        return rcvmtMapper.selectRcvmts(req);
    }

    /**지정한 ID의 수납 정보를 반환한다.<br />
     * @param req 수납 대장 조회 조건
     * @return 수납 정보
     */
    public DataObject getRcvmtInfo(RcvmtQuery req) {
        return rcvmtMapper.selectRcvmtInfo(req);
    }

    /**지정한 ID의 부과 정보를 반환한다.<br />
     * @param req 수납 대장 조회 조건
     * @return 수납 정보
     */
    public DataObject getLevyInfo(RcvmtQuery req) {
        return rcvmtMapper.selectLevyInfo(req);
    }

    /**수납 대장 정보를 등록한다.
     * @param rcvmt 수납 대장
     * @return 저장 여부
     * <ul><li>저장됐으면 true</li>
     *     <li>그렇지 않으면 false</li>
     * </ul>
     */
    public String createRcvmt(Rcvmt rcvmt) {
        // 변수 선언
        int rtnNocs = -1;                                                       // 처리 결과 건수
        String rtnMsg = "";                                                     // 처리 결과 메시지

        // 부과 ID로 부과 정보 조회
        DataObject levyInfo = levyMapper.selectLevyAmt(new LevyQuery().setLevyId(rcvmt.getLevyId()));

        rcvmt.setDeptCd(levyInfo.string("DEPT_CD"));                            // 부서 코드
        rcvmt.setSpclBizCd(levyInfo.string("SPCL_BIZ_CD"));                     // 특별회계 사업 코드
        rcvmt.setLastPcptax(toInt(levyInfo.get("LAST_PCPTAX")));         // 최종 본세
        rcvmt.setLastAdamt(toInt(levyInfo.get("LAST_ADAMT")));           // 최종 가산금

        // 입력된 수납 금액으로 수납 본세, 수납 가산금 나누기
        String rcvmtSeCd = "";                                    // 수납 구분 코드(FIM041)
        if (rcvmt.getLastPcptax() + rcvmt.getLastAdamt() == 0) {                            // 최종 금액이 0원 이라면..
            rcvmt.setRcvmtPcptax(0);                                            // 수납 본세
            rcvmt.setRcvmtAdamt(0);                                             // 수납 가산금
            rcvmtSeCd = "02";                                                   // 수납 구분 코드 = 이중수납
        } else if (rcvmt.getLastPcptax() + rcvmt.getLastAdamt() == rcvmt.getRcvmtAmt()) {    // 최종 금액과 수납 금액이 일치한다면..
            rcvmt.setRcvmtPcptax(rcvmt.getLastPcptax());                        // 수납 본세
            rcvmt.setRcvmtAdamt(rcvmt.getLastAdamt());                          // 수납 가산금
            rcvmtSeCd = "01";                                                   // 수납 구분 코드 = 완납
        } else if (rcvmt.getLastPcptax() + rcvmt.getLastAdamt() < rcvmt.getRcvmtAmt()) {    // 최종 금액보다 수납 금액이 많다면..
            rcvmt.setRcvmtPcptax(rcvmt.getLastPcptax());                        // 수납 본세
            rcvmt.setRcvmtAdamt(rcvmt.getLastAdamt());                          // 수납 가산금
            rcvmtSeCd = "04";                                                   // 수납 구분 코드 = 과납
        } else if (rcvmt.getLastPcptax() + rcvmt.getLastAdamt() > rcvmt.getRcvmtAmt()) {    // 최종 금액보다 수납 금액이 적다면..
            if (rcvmt.getRcvmtAmt() > rcvmt.getLastPcptax()) {
                rcvmt.setRcvmtPcptax(rcvmt.getLastPcptax());                        // 수납 본세
                rcvmt.setRcvmtAdamt(rcvmt.getRcvmtAmt() - rcvmt.getLastPcptax());   // 수납 가산금
            } else {
                rcvmt.setRcvmtPcptax(rcvmt.getRcvmtAmt());                      // 수납 본세
                rcvmt.setRcvmtAdamt(0);                                         // 수납 가산금
            }
            rcvmtSeCd = "06";                                                   // 수납 구분 코드 = 일부수납
        } else {
            rtnMsg = "[F] 수납 등록 중 수납금액 계산에 실패하였습니다.";        // 예외를 발생시켜서 DB Rollback
            return rtnMsg;
        }

        // 수납 구분 코드가 입력 되지 않았다면..
        if (rcvmt.getRcvmtSeCd() == null) {
            rcvmt.setRcvmtSeCd(rcvmtSeCd);
        }

        // 수납(TB_RCVMT) 대장을 등록한다.
        rtnNocs = rcvmtMapper.insertRcvmt(rcvmt);
        if (rtnNocs != 1) {
            throw new RuntimeException("수납 대장 등록에 실패하였습니다.");        // 예외를 발생시켜서 DB Rollback
        }

        if (!rcvmtSeCd.equals("02")) {        // 이중 수납이 아니라면..
            Levy levy = new Levy();
            levy.setLevyId(levyInfo.string("LEVY_ID"));                         // 부과 ID
            levy.setRcvmtPcptax(rcvmt.getRcvmtPcptax());                        // 수납 본세
            levy.setRcvmtAdamt(rcvmt.getRcvmtAdamt());                          // 수납 가산금
            levy.setLastAmt(rcvmt.getLastPcptax() + rcvmt.getLastAdamt() - rcvmt.getRcvmtPcptax() - rcvmt.getRcvmtAdamt());  // 최종 금액
            levy.setRcvmtYmd(rcvmt.getRcvmtYmd());                              // 수납 일자
            levy.setPayMthdSeCd(rcvmt.getPayMthdSeCd());                        // 납부 방법 구분 코드

            // 부과(TB_LEVY) 대장에 수납금액을 수정한다.
            rtnNocs = levyMapper.updateLevyRcvmt(levy);
            if (rtnNocs != 1) {
                throw new RuntimeException("부과 대장에 수납금액 수정에 실패하였습니다.");    // 예외를 발생시켜서 DB Rollback
            }

            // 합계 금액이 0이면서 완납(01) 또는 과납(04)이라면 단속 상태 코드를 수정한다.
            if (levy.getLastAmt() == 0 && (rcvmtSeCd.equals("01") || rcvmtSeCd.equals("04"))) {
                // 단속 상태 이력(TB_CRDN_STTS_HSTRY) 대장에 등록하고, 단속(TB_CRDN) 대장을 수정한다.
                String newCrdnSttsCd = "";
                if (toInt(levyInfo.get("CRDN_STTS_CD")) >= 11 && toInt(levyInfo.get("CRDN_STTS_CD")) <= 45) {
                    newCrdnSttsCd = "71";        // 사전통보수납
                } else if (levyInfo.string("CRDN_STTS_CD").equals("51")) {
                    newCrdnSttsCd = "72";        // 부과수납
                } else if (levyInfo.string("CRDN_STTS_CD").equals("52")) {
                    newCrdnSttsCd = "73";        // 독촉수납
                } else if (levyInfo.string("CRDN_STTS_CD").equals("53")) {
                    newCrdnSttsCd = "74";        // 압류예정수납
                } else if (levyInfo.string("CRDN_STTS_CD").equals("54")) {
                    newCrdnSttsCd = "74";        // 압류예정수납
                } else if (levyInfo.string("CRDN_STTS_CD").equals("55")) {
                    newCrdnSttsCd = "75";        // 압류수납
                }

                CrdnSttsHstry crdnSttsHstry = new CrdnSttsHstry();
                crdnSttsHstry.setCrdnId(levyInfo.string("CRDN_ID"));                // 단속 ID
                crdnSttsHstry.setBfrSttsCd(levyInfo.string("CRDN_STTS_CD"));        // 단속 상태 코드
                crdnSttsHstry.setBfrSttsChgDt(levyInfo.string("CRDN_STTS_CHG_DT")); // 단소 상태 변경 일시
                crdnSttsHstry.setCrdnSttsCd(newCrdnSttsCd);                         // 단속상태코드(FIM010)
                crdnSttsHstry.setTaskDtlId(rcvmt.getRcvmtId());                     // 수납 ID

                // 단속 상태 이력(TB_CRDN_STTS_HSTRY) 대장에 등록하고, 단속(TB_CRDN) 대장을 수정한다.
                boolean rtnScs = crdnSttsHstryBean.createHstryUpdateCrdnSttsCd(crdnSttsHstry);
                if (!rtnScs) {
                    throw new RuntimeException("수납 등록 중 단속 대장의 단속상태 수정에 실패하였습니다.");    // 예외를 발생시켜서 DB Rollback
                }
            }
        }

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

    /**수납 대장에 수납취소 정보를 등록하고, 과오납 정보를 등록을 한다.
     * @param rcvmt 수납 대장
     * @return 저장 여부
     * <ul><li>저장됐으면 true</li>
     *     <li>그렇지 않으면 false</li>
     * </ul>
     */
    public String updateRcvmtRtrcn(Rcvmt rcvmt) {
        // 변수 선언
        String rtnMsg = "";

        // 수납 취소
        rtnMsg = updateRcvmtCancle(rcvmt);
        if (!rtnMsg.contains("[S]")) {
            throw new RuntimeException(rtnMsg);                                    // 예외를 발생시켜서 DB Rollback
        }

        // 과오납 등록


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

    /**수납 대장에 수납취소 정보를 등록한다.
     * @param rcvmt 수납 대장
     * @return 저장 여부
     * <ul><li>저장됐으면 true</li>
     *     <li>그렇지 않으면 false</li>
     * </ul>
     */
    public String updateRcvmtCancle(Rcvmt rcvmt) {
        // 변수 선언
        int rtnNocs = -1;                                                        // 처리 결과 건수

        // 수납 ID로 수납 정보 조회
        DataObject rcvmtInfo = rcvmtMapper.selectRcvmtInfo(new RcvmtQuery().setRcvmtId(rcvmt.getRcvmtId()).setDelYn("N"));

        // 수납(TB_RCVMT) 대장에 수납취소 정보를 수정한다.
        rtnNocs = rcvmtMapper.updateRcvmtRtrcn(rcvmt);
        if (rtnNocs != 1) {
            throw new RuntimeException("수납 대장에 수납취소 정보 수정에 실패하였습니다.");    // 예외를 발생시켜서 DB Rollback
        }

        // 수납구분코드(RCVMT_SE_CD)가 이중 수납이 아니라면..
        if (!rcvmtInfo.string("RCVMT_SE_CD").equals("02")) {
            // 부과 ID로 단속, 부과 정보 조회
            DataObject levyInfo = levyMapper.selectLevyAmt(new LevyQuery().setLevyId(rcvmtInfo.string("LEVY_ID")));

            int rcvmtPcptax = toInt(levyInfo.get("RCVMT_PCPTAX")) - toInt(rcvmtInfo.get("RCVMT_PCPTAX")); // 부과 수납본세   - 수납 수납본세
            int rcvmtAdamt = toInt(levyInfo.get("RCVMT_ADAMT")) - toInt(rcvmtInfo.get("RCVMT_ADAMT"));    // 부과 수납가산금 - 수납 수납가산금
            int lastAmt = toInt(levyInfo.get("LAST_AMT"))
                            + toInt(rcvmtInfo.get("RCVMT_PCPTAX")) + toInt(rcvmtInfo.get("RCVMT_ADAMT")); // 최종 금액

            Levy levy = new Levy();
            levy.setLevyId(levyInfo.string("LEVY_ID"));                         // 부과 ID
            levy.setRcvmtPcptax(rcvmtPcptax);                                   // 수납 본세
            levy.setRcvmtAdamt(rcvmtAdamt);                                     // 수납 가산금
            levy.setLastAmt(lastAmt);                                           // 최종 금액

            // 수납 금액이 0이면서.. 완납(01) 또는 과납(04)이라면 수납 일자를 NULL로 수정한다.
            if (rcvmtPcptax + rcvmtAdamt == 0 && "01,04".contains(rcvmtInfo.string("RCVMT_SE_CD"))) {
                levy.setRcvmtYmd(null);                                         // 수납 일자
                levy.setPayMthdSeCd(null);                                      // 납부 방법 구분 코드
            } else {
                levy.setRcvmtYmd(levyInfo.string("RCVMT_YMD"));                 // 수납 일자
                levy.setPayMthdSeCd(levyInfo.string("PAY_MTHD_SE_CD"));         // 납부 방법 구분 코드
            }

            // 부과(TB_LEVY) 대장에 수납금액 정보를 수정한다.
            rtnNocs = levyMapper.updateLevyRcvmt(levy);
            if (rtnNocs != 1) {
                throw new RuntimeException("부과 대장에 수납금액 수정에 실패하였습니다."); // 예외를 발생시켜서 DB Rollback
            }

            // 단속 대장 수정 ///////////////////////////////////////////////////////////
            // 합계 금액이 0보다 크면서 완납(01) 또는 과납(04)이라면 단속 상태 코드를 수정한다.
            if (levy.getLastAmt() > 0 && "01,04".contains(rcvmtInfo.string("RCVMT_SE_CD"))) {
                // 단속 상태 이력(TB_CRDN_STTS_HSTRY) 대장에 등록하고, 단속(TB_CRDN) 대장을 수정한다.
                String newCrdnSttsCd = switch (levyInfo.string("CRDN_STTS_CD")) {
                case "71" -> "42"; // 사전통보 발송
                case "72" -> "51"; // 부과
                case "73" -> "52"; // 독촉
                case "74" -> "54"; // 압류대상
                case "75" -> "55"; // 압류
                default -> "";
                };

                CrdnSttsHstry crdnSttsHstry = new CrdnSttsHstry();
                crdnSttsHstry.setCrdnId(levyInfo.string("CRDN_ID"));                // 단속 ID
                crdnSttsHstry.setBfrSttsCd(levyInfo.string("CRDN_STTS_CD"));        // 단속 상태 코드
                crdnSttsHstry.setBfrSttsChgDt(levyInfo.string("CRDN_STTS_CHG_DT"));    // 단소 상태 변경 일시
                crdnSttsHstry.setCrdnSttsCd(newCrdnSttsCd);                            // 단속상태코드(FIM010)
                crdnSttsHstry.setTaskDtlId(rcvmt.getRcvmtId());                        // 수납 ID

                // 단속 상태 이력(TB_CRDN_STTS_HSTRY) 대장에 등록하고, 단속(TB_CRDN) 대장을 수정한다.
                boolean rtnScs = crdnSttsHstryBean.createHstryUpdateCrdnSttsCd(crdnSttsHstry);
                if (!rtnScs) {
                    throw new RuntimeException("수납 등록 중 단속 대장의 단속상태 수정에 실패하였습니다.");    // 예외를 발생시켜서 DB Rollback
                }
            }
        }

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

    /**수납 대장 정보를 삭제한다.
     * @param rcvmt 수납 대장
     * @return 저장 여부
     * <ul><li>저장됐으면 true</li>
     *     <li>그렇지 않으면 false</li>
     * </ul>
     */
    public String removeRcvmt(Rcvmt rcvmt) {
        //
        boolean rtnScs = false;                                                 // 처리 결과 여부

        // 수납 ID로 수납 정보 조회
        DataObject rcvmtInfo = rcvmtMapper.selectRcvmtInfo(new RcvmtQuery().setRcvmtId(rcvmt.getRcvmtId()));

        if (!rcvmtInfo.string("RCVMT_INPT_SE_CD").equals("03")) {
            throw new RuntimeException("수납입력구분이 수기등록 자료만 삭제가 가능합니다.");  // 예외를 발생시켜서 DB Rollback
        }
        // 수납(TB_RCVMT) 대장을 삭제한다.
        int rtnNocs = rcvmtMapper.deleteRcvmt(rcvmt);
        if (rtnNocs != 1) {
            throw new RuntimeException("수납 대장 삭제에 실패하였습니다.");   // 예외를 발생시켜서 DB Rollback
        }

        // 부과(TB_LEVY) 대장 수납 금액 수정한다.
        int rcvmtPcptax = toInt(rcvmtInfo.get("L_RCVMT_PCPTAX")) - toInt(rcvmtInfo.get("RCVMT_PCPTAX"));
        int rcvmtAdamt = toInt(rcvmtInfo.get("L_RCVMT_ADAMT")) - toInt(rcvmtInfo.get("RCVMT_ADAMT"));

        Levy levy = new Levy();
        levy.setLevyId(rcvmtInfo.string("LEVY_ID"));                            // 부과 ID
        levy.setRcvmtPcptax(rcvmtPcptax);                                       // 수납 본세
        levy.setRcvmtAdamt(rcvmtAdamt);                                         // 수납 가산금
        levy.setLastAmt(toInt(rcvmtInfo.get("L_LEVY_PCPTAX")) + toInt(rcvmtInfo.get("L_LEVY_ADAMT"))
                - rcvmtPcptax - rcvmtAdamt
                - toInt(rcvmtInfo.get("L_RDAMT_PCPTAX")) - toInt(rcvmtInfo.get("L_RDAMT_ADAMT"))); // 최종 금액

        // 부과(TB_LEVY) 대장에 수납금액을 수정한다.
        rtnNocs = levyMapper.updateLevyRcvmt(levy);
        if (rtnNocs != 1) {
            throw new RuntimeException("부과 대장에 수납금액 수정에 실패하였습니다.");    // 예외를 발생시켜서 DB Rollback
        }

        // 최종 금액이 0보다 크다면 이전 처리상태로 변경한다.
        if (levy.getLastAmt() > 0) {
            // 단속 상태 이력(TB_CRDN_STTS_HSTRY) 대장의 사용 여부를 "N"으로 처리하고, 단속(TB_CRDN) 대장의 단속 상태 코드를 수정한다.
            CrdnSttsHstry crdnSttsHstry = new CrdnSttsHstry();
            crdnSttsHstry.setCrdnId(rcvmtInfo.string("CRDN_ID"));               // 단속 ID
            crdnSttsHstry.setCrdnSttsCd(rcvmtInfo.string("CRDN_STTS_CD"));      // 단속 상태 코드
            crdnSttsHstry.setEtcCn(rcvmt.getDelRsn());                          // 기타 내용 - 삭제 사유
            crdnSttsHstry.setUseYn("N");                                        // 사용 여부

            rtnScs = crdnSttsHstryBean.removeHstryUpdateCrdnSttsCd(crdnSttsHstry);
            if (!rtnScs) {
                throw new RuntimeException("[F] 발송상세 삭제 작업중 단속 대장의 단속상태 변경에 실패하였습니다."); // 예외를 발생시켜서 DB Rollback
            }
        }

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

}
