package com.kingdee.eas.custom.integration.riskcontrol.service.impl;

import com.kingdee.bos.BOSException;
import com.kingdee.bos.Context;
import com.kingdee.bos.dao.ormapping.ObjectUuidPK;
import com.kingdee.eas.basedata.person.IPerson;
import com.kingdee.eas.basedata.person.PersonFactory;
import com.kingdee.eas.basedata.person.PersonInfo;
import com.kingdee.eas.common.EASBizException;
import com.kingdee.eas.custom.compensation.DonationDeductCollection;
import com.kingdee.eas.custom.compensation.DonationDeductFactory;
import com.kingdee.eas.custom.compensation.DonationDeductInfo;
import com.kingdee.eas.custom.compensation.IDonationDeduct;
import com.kingdee.eas.custom.compensation.basedata.ViolationTypeCollection;
import com.kingdee.eas.custom.compensation.basedata.ViolationTypeFactory;
import com.kingdee.eas.custom.compensation.basedata.ViolationTypeInfo;
import com.kingdee.eas.custom.compensation.bizEnum.SyncStatusEnum;
import com.kingdee.eas.custom.integration.base.util.SHRApiUtils;
import com.kingdee.eas.custom.integration.beisen.dto.FailDetail;
import com.kingdee.eas.custom.integration.riskcontrol.dto.BatchComplaintDonationDeductRequest;
import com.kingdee.eas.custom.integration.riskcontrol.dto.ComplaintDonationDeductRequest;
import com.kingdee.eas.custom.integration.riskcontrol.dto.ComplaintDonationDeductResponse;
import com.kingdee.eas.custom.integration.riskcontrol.exception.RiskControlErrorCodes;
import com.kingdee.eas.custom.integration.riskcontrol.exception.RiskControlIntegrationException;
import com.kingdee.eas.custom.integration.riskcontrol.service.ComplaintDonationDeductService;
import com.kingdee.eas.custom.integration.riskcontrol.validator.ComplaintDonationDeductValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 投诉乐捐扣除同步服务实现。
 * 以 bizId 为幂等键写入 DonationDeduct.number，已暂存状态下可更新，非暂存状态拒绝更新。
 */
@Service
public class ComplaintDonationDeductServiceImpl implements ComplaintDonationDeductService {

    private static final Logger logger = LoggerFactory.getLogger(ComplaintDonationDeductServiceImpl.class);

    @Override
    public ComplaintDonationDeductResponse writeComplaintDonationDeduct(
            BatchComplaintDonationDeductRequest request) {
        logger.info("开始处理投诉乐捐扣除同步 - 记录数: {}",
                request.getRecords() != null ? request.getRecords().size() : 0);

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        ComplaintDonationDeductResponse response = new ComplaintDonationDeductResponse();
        List<ComplaintDonationDeductRequest> records = request.getRecords();

        if (records == null || records.isEmpty()) {
            logger.warn("投诉乐捐扣除同步 - 记录列表为空");
            return response;
        }

        Context ctx;
        try {
            ctx = SHRApiUtils.getContext();
        } catch (Exception e) {
            logger.error("无法获取 BOS Context", e);
            response.setFailCount(records.size());
            response.setTotalCount(records.size());
            for (ComplaintDonationDeductRequest r : records) {
                response.addFailDetail(new FailDetail(r.getBizId(), "无法获取 BOS Context: " + e.getMessage()));
            }
            return response;
        }

        try {
            IPerson iPerson = PersonFactory.getLocalInstance(ctx);
            IDonationDeduct iDonationDeduct = DonationDeductFactory.getLocalInstance(ctx);

            // 加载所有记录 number -> Info 映射，用于幂等判断
            Map<String, DonationDeductInfo> existingMap = loadExistingByBizId(iDonationDeduct);
            // 违规类型缓存
            Map<String, ViolationTypeInfo> violationTypeCache = new HashMap<>();

            int totalCount = records.size();
            int successCount = 0;
            int failCount = 0;

            for (ComplaintDonationDeductRequest record : records) {
                String bizId = record.getBizId();
                try {
                    // 1. 字段校验
                    String validateError = ComplaintDonationDeductValidator.validateAndGetError(record);
                    if (validateError != null) {
                        failCount++;
                        response.addFailDetail(new FailDetail(bizId, "校验失败: " + validateError));
                        logger.warn("投诉乐捐扣除校验失败 - bizId: {}, 原因: {}", bizId, validateError);
                        continue;
                    }

                    Date voucherDate = sdf.parse(record.getVoucherDate());

                    // 2. 查找人员
                    PersonInfo person = iPerson.getPersonByStr("number", record.getPersonNumber());
                    if (person == null) {
                        failCount++;
                        response.addFailDetail(new FailDetail(bizId,
                                "sHR系统中不存在工号为" + record.getPersonNumber() + "的人员"));
                        continue;
                    }

                    // 3. 查找违规类型
                    ViolationTypeInfo violationType = lookupViolationType(
                            ctx, record.getViolationTypeCode(), violationTypeCache);
                    if (violationType == null) {
                        failCount++;
                        response.addFailDetail(new FailDetail(bizId,
                                "sHR系统中不存在编码为" + record.getViolationTypeCode() + "的违规类型"));
                        continue;
                    }

                    // 4. 按 bizId 判断新建还是更新
                    DonationDeductInfo existing = existingMap.get(bizId);
                    if (existing != null) {
                        // 已存在，仅暂存状态可更新
                        if (existing.getState() != SyncStatusEnum.SAVED) {
                            failCount++;
                            response.addFailDetail(new FailDetail(bizId,
                                    "单据状态为" + existing.getState().getAlias() + "，非暂存状态不可更新"));
                            continue;
                        }
                        applyUpdate(existing, record, person, violationType, voucherDate);
                        iDonationDeduct.update(new ObjectUuidPK(existing.getId()), existing);
                        existingMap.put(bizId, existing);
                        successCount++;
                        logger.info("投诉乐捐扣除更新成功 - bizId: {}, 人员: {}", bizId, record.getPersonNumber());
                    } else {
                        // 不存在，新建
                        DonationDeductInfo info = new DonationDeductInfo();
                        info.setNumber(bizId);
                        applyUpdate(info, record, person, violationType, voucherDate);
                        info.setState(SyncStatusEnum.SAVED);
                        tryAddnewOrUpdate(iDonationDeduct, existingMap, bizId, info, record,
                                person, violationType, voucherDate);
                        successCount++;
                        logger.info("投诉乐捐扣除新增成功 - bizId: {}, 人员: {}", bizId, record.getPersonNumber());
                    }

                } catch (Exception e) {
                    failCount++;
                    String reason = e.getMessage() != null ? e.getMessage() : "未知错误";
                    response.addFailDetail(new FailDetail(bizId, reason));
                    logger.warn("投诉乐捐扣除同步失败 - bizId: {}, 原因: {}", bizId, reason);
                }
            }

            response.setTotalCount(totalCount);
            response.setSuccessCount(successCount);
            response.setFailCount(failCount);

            logger.info("投诉乐捐扣除同步完成 - 总数: {}, 成功: {}, 失败: {}",
                    totalCount, successCount, failCount);
        } catch (BOSException e) {
            throw new RiskControlIntegrationException(
                    RiskControlErrorCodes.SAVE_ERROR, "数据保存失败: " + e.getMessage(), e);
        }
        return response;
    }

    /** 加载所有记录，构建 number -> Info 映射。 */
    private Map<String, DonationDeductInfo> loadExistingByBizId(IDonationDeduct iDonationDeduct)
            throws BOSException {
        Map<String, DonationDeductInfo> map = new HashMap<>();
        DonationDeductCollection col = iDonationDeduct.getDonationDeductCollection();
        if (col != null) {
            for (int i = 0; i < col.size(); i++) {
                DonationDeductInfo info = col.get(i);
                if (info.getNumber() != null) {
                    map.put(info.getNumber(), info);
                }
            }
        }
        return map;
    }

    /** 新增记录，遇到主键冲突时转为更新。 */
    private void tryAddnewOrUpdate(IDonationDeduct iDonationDeduct,
                                   Map<String, DonationDeductInfo> existingMap,
                                   String bizId, DonationDeductInfo info,
                                   ComplaintDonationDeductRequest record,
                                   PersonInfo person, ViolationTypeInfo violationType,
                                   Date voucherDate) throws BOSException {
        try {
            iDonationDeduct.addnew(info);
            existingMap.put(bizId, info);
            logger.info("投诉乐捐扣除新增成功 - bizId: {}", bizId);
        } catch (BOSException e) {
            DonationDeductInfo existing = findByNumber(iDonationDeduct, bizId);
            if (existing != null) {
                if (existing.getState() != SyncStatusEnum.SAVED) {
                    throw new RiskControlIntegrationException(
                            RiskControlErrorCodes.UPDATE_NOT_ALLOWED, "单据状态非暂存，不可更新: " + bizId);
                }
                try {
                    applyUpdate(existing, record, person, violationType, voucherDate);
                    iDonationDeduct.update(new ObjectUuidPK(existing.getId()), existing);
                    existingMap.put(bizId, existing);
                    logger.info("冲突后转为更新 - bizId: {}", bizId);
                } catch (EASBizException ex) {
                    throw new RiskControlIntegrationException(
                            RiskControlErrorCodes.SAVE_ERROR, "更新失败: " + e.getMessage(), e);
                }
            } else {
                throw new RiskControlIntegrationException(
                        RiskControlErrorCodes.SAVE_ERROR, "保存失败: " + e.getMessage(), e);
            }
        } catch (Exception e) {
            throw new RiskControlIntegrationException(
                    RiskControlErrorCodes.SAVE_ERROR, "保存失败: " + e.getMessage(), e);
        }
    }

    /** 按 number 查找已有记录。 */
    private DonationDeductInfo findByNumber(IDonationDeduct iDonationDeduct, String number)
            throws BOSException {
        DonationDeductCollection col = iDonationDeduct.getDonationDeductCollection(
                "where number = '" + escapeSql(number) + "'");
        if (col != null && !col.isEmpty()) {
            return col.get(0);
        }
        return null;
    }

    /** 将请求字段应用到 DonationDeductInfo。 */
    private void applyUpdate(DonationDeductInfo info, ComplaintDonationDeductRequest record,
                             PersonInfo person, ViolationTypeInfo violationType, Date voucherDate) {
        info.setPerson(person);
        info.setEffectDate(voucherDate);
        info.setComplaintFineAmount(record.getComplaintFineAmount());
        info.setViolationDonAmount(record.getViolationDonationAmount());
        info.setViolationType(violationType);
        if (record.getRemark() != null) {
            info.setDescription(record.getRemark());
        }
    }

    /** 按编码查找违规类型，带缓存。 */
    private ViolationTypeInfo lookupViolationType(Context ctx, String code,
                                                   Map<String, ViolationTypeInfo> cache)
            throws BOSException {
        if (cache.containsKey(code)) {
            return cache.get(code);
        }
        ViolationTypeCollection col = ViolationTypeFactory.getLocalInstance(ctx)
                .getViolationTypeCollection("where number = '" + escapeSql(code) + "'");
        if (col != null && !col.isEmpty()) {
            ViolationTypeInfo info = col.get(0);
            cache.put(code, info);
            return info;
        }
        return null;
    }

    private String escapeSql(String s) {
        return s.replace("'", "''");
    }
}
