package com.kingdee.eas.custom.compensation.startingsalary.backgroundtransaction;

import com.kingdee.bos.BOSException;
import com.kingdee.bos.Context;
import com.kingdee.bos.metadata.entity.EntityViewInfo;
import com.kingdee.bos.metadata.entity.FilterInfo;
import com.kingdee.bos.metadata.entity.FilterItemInfo;
import com.kingdee.bos.metadata.entity.SelectorItemCollection;
import com.kingdee.bos.metadata.query.util.CompareType;
import com.kingdee.bos.util.BOSUuid;
import com.kingdee.eas.base.permission.UserInfo;
import com.kingdee.eas.basedata.assistant.CurrencyCollection;
import com.kingdee.eas.basedata.assistant.CurrencyFactory;
import com.kingdee.eas.basedata.assistant.CurrencyInfo;
import com.kingdee.eas.basedata.org.AdminOrgUnitInfo;
import com.kingdee.eas.basedata.org.HROrgUnitInfo;
import com.kingdee.eas.basedata.org.PositionInfo;
import com.kingdee.eas.basedata.person.PersonInfo;
import com.kingdee.eas.common.EASBizException;
import com.kingdee.eas.custom.certification.utils.DateUtil;
import com.kingdee.eas.hr.contract.EContractSignState;
import com.kingdee.eas.hr.emp.EmployeeOtherContractCollection;
import com.kingdee.eas.hr.emp.EmployeeOtherContractFactory;
import com.kingdee.eas.hr.emp.EmployeeOtherContractInfo;
import com.kingdee.eas.hr.emp.IEmployeeOtherContract;
import com.kingdee.shr.base.syssetting.context.SHRContext;
import com.kingdee.shr.base.syssetting.exception.SHRWebException;
import com.kingdee.shr.cmpdesign.BatchAdjustBillEntryInfo;
import com.kingdee.shr.cmpdesign.BatchAdjustBillInfo;
import com.kingdee.shr.cmpdesign.BatchAdjustFacadeFactory;
import com.kingdee.shr.cmpdesign.app.v2.mapping.AdjustItemMappingSchemeCollection;
import com.kingdee.shr.cmpdesign.app.v2.mapping.AdjustItemMappingSchemeFactory;
import com.kingdee.shr.cmpdesign.app.v2.mapping.AdjustItemMappingSchemeInfo;
import com.kingdee.shr.cmpdesign.app.v2.mapping.IAdjustItemMappingScheme;
import com.kingdee.shr.compensation.*;
import com.kingdee.util.StringUtils;
import org.apache.log4j.Logger;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 自动生成批量薪资调整单Facade实现类
 * <p>
 * 业务场景：入职起薪场景，读取【员工其他电子合同】已签署单据，提取合同内薪资字段，
 * 通过薪资项映射方案转换为系统标准薪资项，自动创建批量薪资调整单，并且调用薪资工具直接生效单据。
 * 支持三种查询筛选方式：单据ID集合 / 向前追溯天数 / 自定义起止日期
 * </p>
 * @author 自定义开发
 */
public class CreateBatchAdjustBillFacadeControllerBean extends AbstractCreateBatchAdjustBillFacadeControllerBean {
    // 默认币别编码：人民币
    private static final String DEFAULT_CURRENCY_NUMBER = "BB01";
    private static final Logger logger =
            Logger.getLogger("com.kingdee.eas.custom.compensation.startingsalary.backgroundtransaction.CreateBatchAdjustBillFacadeControllerBean");
    // 起薪基础配置参数对象，存放薪资映射配置
    BasicSalaryParameters parameters = null;

    /**
     * 自动创建批量薪资调整单入口方法
     * <p>
     * 执行流程：
     * 1. 根据传入参数构建查询条件，筛选【员工其他合同】已签署单据；
     * 2. 循环每条合同，提取员工、入职日期；
     * 3. 创建批量薪资调整单表头；
     * 4. 获取员工当前有效薪酬任职关系；
     * 5. 根据薪资映射配置，读取合同自定义字段金额；
     * 6. 通过映射方案匹配系统薪资项，构建调薪明细；
     * 7. 调用外部Facade，直接提交生效薪资调整单；
     * </p>
     * @param ctx BOS上下文
     * @param otherContractId 员工其他合同ID，多个ID英文逗号分隔；为空则使用日期范围查询
     * @param leadDays 向前追溯查询天数；otherContractId为空时生效
     * @param startDate 自定义起始日期；字符串格式 yyyy-MM-dd
     * @param endDate 自定义结束日期；字符串格式 yyyy-MM-dd
     * @throws BOSException BOS框架异常
     */
    @Override
    public void createBill(Context ctx, String otherContractId, int leadDays, String startDate, String endDate) throws BOSException {
        // 初始化起薪参数配置
        parameters = new BasicSalaryParameters(ctx);
        EntityViewInfo viewInfo = new EntityViewInfo();
        FilterInfo filterInfo = new FilterInfo();

        // 条件分支1：传入单据ID集合，优先精确查询指定合同单据
        if (otherContractId != null && otherContractId.length() > 0) {
            otherContractId = otherContractId.trim();
            String[] split = otherContractId.split(",");
            // 使用Set对单据ID去重
            Set<String> idSet = new HashSet<String>();
            for (String id : split) {
                idSet.add(id.trim());
            }
            filterInfo.getFilterItems().add(new FilterItemInfo("id", idSet, CompareType.INCLUDE));
        }
        // 条件分支2：无单据ID，使用向前追溯天数范围查询
        else if (leadDays != 0) {
            Date now = new Date();
            // 创建时间 <= 当前日期
            filterInfo.getFilterItems().add(new FilterItemInfo("createTime", now, CompareType.LESS_EQUALS));
            // 计算追溯起始日期：当前日期向前推leadDays天
            Date before = DateUtil.add(now, -leadDays);
            // 创建时间 >= 追溯起始日期
            filterInfo.getFilterItems().add(new FilterItemInfo("createTime", before, CompareType.GREATER_EQUALS));
        }
        // 条件分支3：传入起止日期，使用自定义日期区间查询
        else if (startDate != null && startDate.length() > 0 && endDate != null && endDate.length() > 0) {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            Date start = null;
            try {
                start = format.parse(startDate);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
            Date end = null;
            try {
                end = format.parse(endDate);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
            filterInfo.getFilterItems().add(new FilterItemInfo("createTime", start, CompareType.GREATER_EQUALS));
            filterInfo.getFilterItems().add(new FilterItemInfo("createTime", end, CompareType.LESS_EQUALS));
        }
        // 未传入任何有效查询条件，抛出异常终止
        else {
            throw new BOSException("请选择查询的日期范围");
        }
        // 固定过滤条件：电子合同签署状态=已完成
        filterInfo.getFilterItems().add(new FilterItemInfo("SignStatus", EContractSignState.COMPLETE_VALUE, CompareType.EQUALS));

        viewInfo.setFilter(filterInfo);
        SelectorItemCollection selectorItemCollection = new SelectorItemCollection();
        selectorItemCollection.add("*");
        viewInfo.setSelector(selectorItemCollection);
        IEmployeeOtherContract iEmployeeOtherContract = EmployeeOtherContractFactory.getLocalInstance(ctx);
        // 查询符合条件的员工其他合同集合
        EmployeeOtherContractCollection employeeOtherContractCollection = iEmployeeOtherContract.getEmployeeOtherContractCollection(viewInfo);
        // 获取系统默认币别（人民币）
        CurrencyInfo currencyInfo = getDefaultCurrency(ctx);

        // 遍历每一条已签署的其他合同，生成薪资调整单
        for (int i = 0; i < employeeOtherContractCollection.size(); i++) {
            EmployeeOtherContractInfo employeeOtherContractInfo = employeeOtherContractCollection.get(i);
            PersonInfo employee = employeeOtherContractInfo.getEmployee(); // 合同对应的员工
            Date recordDate = employeeOtherContractInfo.getRecordDate();   // 入职日期，作为薪资生效日期

            // 创建批量薪资调整单表头
            BatchAdjustBillInfo batchAdjustBill = this.createBatchAdjustBill(ctx, employee, recordDate);
            // 获取员工当前有效薪酬任职关系
            AdjustEmpORelationInfo adjustEmpORelationInfo = getAdjustEmpORelation(ctx, employee);
            // 获取薪资映射配置：外部标识 -> 实体字段映射关系
            Map<String, Map<String, String>> transferSalary = parameters.getTransferSalary();

            // 循环薪资映射配置，读取合同字段金额
            for (Map.Entry<String, Map<String, String>> entry : transferSalary.entrySet()) {
                String key = entry.getKey();
                Map<String, String> value = entry.getValue();
                System.out.println("key:" + key);
                System.out.println("value:" + value);

                for (Map.Entry<String, String> entry1 : value.entrySet()) {
                    String key1 = entry1.getKey();    // 薪资项外部编码
                    String value1 = entry1.getValue();// 员工其他合同实体属性名

                    System.out.println("key1:" + key1);
                    System.out.println("value1:" + value1);

                    // 从合同单据读取对应薪资字段金额
                    Object o = employeeOtherContractInfo.get(value1);
                    if (o == null) {
                        System.out.println("o为空：" + employeeOtherContractInfo.getId());
                        System.out.println("value1：" + value1);
                        System.out.println("key1：" + key1 + "value1：" + value1 + "o：" + o);
                        continue;
                    }
                    // 根据外部编码匹配系统标准薪资项
                    CmpItemInfo cmpItemInfo = getCmpItemInfo(ctx, key1);
                    try {
                        // 新增薪资调整明细行
                        this.addSalaryAdjustmentEntries(ctx, batchAdjustBill, adjustEmpORelationInfo, cmpItemInfo,
                                currencyInfo, employee, recordDate, new BigDecimal(o.toString()));
                    } catch (SHRWebException e) {
                        System.out.println("生成调薪分录失败");
                        e.printStackTrace();
                    }
                }
            }
            try {
                employeeOtherContractInfo.put("isBatchAdjustBill", true);
                iEmployeeOtherContract.save(employeeOtherContractInfo);
            } catch (EASBizException e) {
                throw new RuntimeException(e);
            }
            try {
                // 调用薪资工具Facade，提交并直接生效薪资调整单
                SalaryUtilsFacadeFactory.getLocalInstance(ctx).
                        submitEffectBatchAdjustBill(batchAdjustBill.getId().toString());
            } catch (Exception e) {
                System.out.println("提交生效调薪单失败！");
                employeeOtherContractInfo.put("isBatchAdjustBill", false);
                employeeOtherContractInfo.put("errorInfo", "提交生效调薪单失败："+e.getMessage());
                try {
                    iEmployeeOtherContract.save(employeeOtherContractInfo);
                } catch (EASBizException ex) {
                    throw new RuntimeException(ex);
                }
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据外部薪资项编码，查询系统标准薪资项信息
     * <p>
     * 查询【薪资调整项映射方案】，实现外部编码与EAS内部薪资项CmpItem的映射转换
     * </p>
     * @param context BOS上下文
     * @param number 薪资项外部编码
     * @return CmpItemInfo 系统标准薪资项；找不到返回null
     */
    public CmpItemInfo getCmpItemInfo(Context context, String number) {
        try {
            // 获取配置中定义的映射方案编码
            String empAdjustItemMappingTableNumber = parameters.getEmpAdjustItemMappingTableNumber();
            IAdjustItemMappingScheme iAdjustItemMappingScheme = AdjustItemMappingSchemeFactory.getLocalInstance(context);
            EntityViewInfo viewInfo = new EntityViewInfo();
            FilterInfo filterInfo = new FilterInfo();
            // 根据映射方案编码、外部薪资项编码过滤
            filterInfo.getFilterItems().add(new FilterItemInfo("bill.number", empAdjustItemMappingTableNumber, CompareType.EQUALS));
            filterInfo.getFilterItems().add(new FilterItemInfo("empAdjNumber", number, CompareType.EQUALS));
            viewInfo.setFilter(filterInfo);

            SelectorItemCollection selectorItemCollection = new SelectorItemCollection();
            selectorItemCollection.add("*");
            selectorItemCollection.add("AdjustItem.CmpItem.*");
            viewInfo.setSelector(selectorItemCollection);

            AdjustItemMappingSchemeCollection adjustItemMappingSchemeCollection = iAdjustItemMappingScheme.getAdjustItemMappingSchemeCollection(viewInfo);
            if (adjustItemMappingSchemeCollection.size() > 0) {
                AdjustItemMappingSchemeInfo adjustItemMappingSchemeInfo = adjustItemMappingSchemeCollection.get(0);
                CmpItemInfo cmpItemInfo = adjustItemMappingSchemeInfo.getCmpItem();
                return cmpItemInfo;
            }
        } catch (BOSException e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    /**
     * 创建批量薪资调整单表头信息
     * @param context BOS上下文
     * @param person 调薪员工
     * @param enrollDate 入职日期（薪资生效日期）
     * @return BatchAdjustBillInfo 薪资调整单表头实体
     * @throws BOSException BOS框架异常
     */
    private BatchAdjustBillInfo createBatchAdjustBill(Context context, PersonInfo person, Date enrollDate)
            throws BOSException {
        BatchAdjustBillInfo batchAdjustBillInfo = new BatchAdjustBillInfo();

        // 设置申请人信息，取当前登录用户
        UserInfo userInfo = (UserInfo) context.get("UserInfo");
        PersonInfo userInfoPerson = userInfo.getPerson();
        batchAdjustBillInfo.setProposer(userInfoPerson);

        // 设置组织信息（固定默认组织，业务上线后按需调整）
        AdminOrgUnitInfo adminOrgUnitInfo = createDefaultOrgUnit();
        batchAdjustBillInfo.setAdminOrg(adminOrgUnitInfo);
        HROrgUnitInfo hrOrgUnitInfo = new HROrgUnitInfo();
        hrOrgUnitInfo.setId(BOSUuid.read(adminOrgUnitInfo.getId().toString()));
        batchAdjustBillInfo.setHrOrgUnit(hrOrgUnitInfo);

        // 单据编号规则：员工姓名_员工编号
        batchAdjustBillInfo.setNumber(person.getName() + "_" + person.getNumber());
        // 申请日期取系统当前时间
        batchAdjustBillInfo.setApplyDate(new Date());

        return batchAdjustBillInfo;
    }

    /**
     * 新增薪资调整单明细条目
     * <p>
     * 组装明细所有字段，关联薪酬任职关系、薪资项、生效日期、调整金额；
     * 查询并绑定人员上一次薪资调整记录，用于薪资变动追溯；
     * 将明细添加至调整单主表明细集合。
     * </p>
     * @param context BOS上下文
     * @param batchAdjustBillInfo 薪资调整单表头
     * @param adjustEmpORelationInfo 薪酬任职关系
     * @param cmpItemInfo 薪资项
     * @param currencyInfo 币别
     * @param person 调薪员工
     * @param enrollDate 薪资生效日期
     * @param amount 调整金额
     * @throws SHRWebException 薪资模块业务异常
     * @throws BOSException BOS框架异常
     */
    private void addSalaryAdjustmentEntries(Context context,
                                            BatchAdjustBillInfo batchAdjustBillInfo,
                                            AdjustEmpORelationInfo adjustEmpORelationInfo,
                                            CmpItemInfo cmpItemInfo, CurrencyInfo currencyInfo,
                                            PersonInfo person, Date enrollDate, BigDecimal amount) throws SHRWebException, BOSException {
        // 从薪酬任职关系获取行政组织、职位
        AdminOrgUnitInfo adminOrg = adjustEmpORelationInfo.getEmpOrgRelation().getAdminOrg();
        PositionInfo position = adjustEmpORelationInfo.getEmpOrgRelation().getPosition();
        BatchAdjustBillEntryInfo entryInfo = new BatchAdjustBillEntryInfo();

        // 明细基础字段赋值
        entryInfo.setAdminOrgUnit(adminOrg);
        entryInfo.setCmpItem(cmpItemInfo);
        entryInfo.setAdjEmpORelation(adjustEmpORelationInfo);
        entryInfo.setCurrency(currencyInfo);
        entryInfo.setEffectDate(enrollDate); // 薪资生效日期=入职日期
        entryInfo.setMoney(amount);
        entryInfo.setPerson(person);
        entryInfo.setPosition(position);

        // 固定发薪HR组织（上线后可按需配置）
        HROrgUnitInfo hrOrgUnitInfo = new HROrgUnitInfo();
        hrOrgUnitInfo.setId(BOSUuid.read("00000000-0000-0000-0000-000000000000CCE7AED4"));
        entryInfo.setPayHrOrgUnit(hrOrgUnitInfo);

        // 备注字段预留，暂时注释
//        if (!StringUtils.isEmpty(remark)) {
//            entryInfo.setRemark(remark);
//        }
        // 查询并绑定人员该薪资项上一次调整记录
        associateLastAdjustRecord(context, entryInfo, person, cmpItemInfo);
        // 将明细行加入单据
        batchAdjustBillInfo.getEntry().add(entryInfo);
    }

    /**
     * 关联人员上一条薪资调整记录
     * <p>
     * 查询历史固定薪资调整记录，赋值给lastInfo，用于薪资变更追溯链条
     * </p>
     * @param context BOS上下文
     * @param entryInfo 薪资调整明细
     * @param person 员工
     * @param cmpItemInfo 薪资项
     * @throws SHRWebException 查询异常
     */
    private void associateLastAdjustRecord(Context context, BatchAdjustBillEntryInfo entryInfo,
                                           PersonInfo person, CmpItemInfo cmpItemInfo) throws SHRWebException {
        try {
            Map<String, Object> lastAdjustInfo = getLastAdjustSalaryAction(person.getId().toString(),
                    cmpItemInfo.getId().toString());
            Object lastAdjustId = lastAdjustInfo.get("id");

            if (lastAdjustId != null && !lastAdjustId.toString().equals("null")) {
                FixAdjustSalaryInfo fixAdjustSalaryInfo = new FixAdjustSalaryInfo();
                fixAdjustSalaryInfo.setId(BOSUuid.read(lastAdjustId.toString()));
                entryInfo.setLastInfo(fixAdjustSalaryInfo);
            }
        } catch (SHRWebException e) {
            throw new SHRWebException("查询人员上次薪资调整记录失败", e);
        }
    }

    /**
     * 获取系统默认币别信息（人民币 BB01）
     * @param context BOS上下文
     * @return CurrencyInfo 币别实体
     * @throws BOSException BOS框架异常
     */
    private CurrencyInfo getDefaultCurrency(Context context) throws BOSException {
        CurrencyCollection currencyCollection = CurrencyFactory.getLocalInstance(context)
                .getCurrencyCollection("where number='" + DEFAULT_CURRENCY_NUMBER + "'");
        if (currencyCollection.size() == 0) {
            throw new RuntimeException("未找到默认币别配置: " + DEFAULT_CURRENCY_NUMBER);
        }
        return currencyCollection.get(0);
    }

    /**
     * 查询人员指定薪资项最近一次生效薪资调整记录
     * @param personIds 人员ID
     * @param itemIds 薪资项ID
     * @return Map<String,Object> 调整记录字段集合，key为字段名
     * @throws SHRWebException 查询异常
     */
    public Map<String, Object> getLastAdjustSalaryAction(String personIds, String itemIds) throws SHRWebException {
        Map<String, Object> adjustMap = null;
        if (!StringUtils.isEmpty(personIds)) {
            try {
                Context ctx = SHRContext.getInstance().getContext();
                // 调用薪资标准Facade查询历史调整记录
                adjustMap = BatchAdjustFacadeFactory
                        .getLocalInstance(ctx)
                        .getLastAdjustSalary(personIds, itemIds, "00000000-0000-0000-0000-000000000000CCE7AED4");
            } catch (EASBizException e) {
                e.printStackTrace();
                throw new SHRWebException("获取上次薪资调整记录失败：" + e.getMessage());
            } catch (BOSException e) {
                e.printStackTrace();
                throw new SHRWebException("获取上次薪资调整记录失败：" + e.getMessage());
            }
        }
        return adjustMap;
    }

    /**
     * 创建默认行政组织单元
     * <p>
     * 当前写死固定ID，项目实施阶段需要根据实际组织主键修改
     * </p>
     * @return AdminOrgUnitInfo 行政组织实体
     */
    private AdminOrgUnitInfo createDefaultOrgUnit() {
        AdminOrgUnitInfo adminOrgUnitInfo = new AdminOrgUnitInfo();
        // TODO 根据实际业务替换组织ID
        adminOrgUnitInfo.setId(BOSUuid.read("00000000-0000-0000-0000-000000000000CCE7AED4"));
        return adminOrgUnitInfo;
    }

    /**
     * 获取员工当前有效薪酬任职关系
     * <p>
     * 查询生效日期大于等于当前系统日期的薪酬任职记录，取第一条
     * </p>
     * @param context BOS上下文
     * @param person 员工
     * @return AdjustEmpORelationInfo 薪酬任职关系；无记录返回null
     * @throws BOSException BOS框架异常
     */
    private AdjustEmpORelationInfo getAdjustEmpORelation(Context context, PersonInfo person) throws BOSException {
        EntityViewInfo empORelationView = new EntityViewInfo();
        SelectorItemCollection selectorItem = new SelectorItemCollection();
        selectorItem.add("*");
        selectorItem.add("empOrgRelation.*");
        empORelationView.setSelector(selectorItem);

        FilterInfo filter = new FilterInfo();
        // leffectDate >= 当前日期：有效在职薪酬任职关系
        filter.getFilterItems().add(new FilterItemInfo("leffectDate", new Date(), CompareType.GREATER_EQUALS));
        filter.getFilterItems().add(new FilterItemInfo("Person.id", person.getId().toString(), CompareType.EQUALS));
        empORelationView.setFilter(filter);

        AdjustEmpORelationCollection relationCollection = AdjustEmpORelationFactory.getLocalInstance(context)
                .getAdjustEmpORelationCollection(empORelationView);

        return relationCollection.size() > 0 ? relationCollection.get(0) : null;
    }
}
