package com.kingdee.eas.custom.recuritment.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.kingdee.bos.BOSException;
import com.kingdee.bos.Context;
import com.kingdee.bos.bsf.service.app.IHRMsfService;
import com.kingdee.bos.metadata.entity.EntityViewInfo;
import com.kingdee.bos.metadata.entity.FilterInfo;
import com.kingdee.bos.metadata.entity.FilterItemCollection;
import com.kingdee.bos.metadata.entity.FilterItemInfo;
import com.kingdee.bos.metadata.entity.SelectorItemCollection;
import com.kingdee.bos.metadata.entity.SelectorItemInfo;
import com.kingdee.bos.metadata.query.util.CompareType;
import com.kingdee.eas.basedata.person.IPerson;
import com.kingdee.eas.basedata.person.PersonCollection;
import com.kingdee.eas.basedata.person.PersonFactory;
import com.kingdee.eas.common.EASBizException;
import com.kingdee.eas.custom.recuritment.utils.BeisenRecruitOwnerApiHelper;
import com.kingdee.eas.custom.recuritment.ApplicantBeisenCollection;
import com.kingdee.eas.custom.recuritment.ApplicantBeisenFactory;
import com.kingdee.eas.custom.recuritment.ApplicantBeisenInfo;
import com.kingdee.eas.custom.recuritment.IApplicantBeisen;
import com.kingdee.eas.hr.ats.AtsUtil;
import com.kingdee.util.StringUtils;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 同步北森应聘者（ApplicantBeisen）招聘负责人 OSF 服务。
 * 业务链路：申请ID → 北森申请(jobId) → 北森职位(hrDutyUser) → 北森员工(staffCode) → sHR人员(recruitOwner)。
 * 待同步判定：recruitOwner（招聘负责人）为空才同步；已有负责人则跳过。
 * 同步结果写入 syncRecruit，成功为「同步成功」，失败为具体原因。
 * OSF 服务名：syncRecruitOwnerService；入参 billIds 为空时走定时扫描。
 *
 * @author qingwu
 */
public class SyncRecruitOwnerService implements IHRMsfService {

    /** 日志记录器 */
    private static final Logger logger = Logger.getLogger(SyncRecruitOwnerService.class);
    /** 同步成功固定文案，写入 syncRecruit 字段，禁止随意修改 */
    private static final String SYNC_SUCCESS = "同步成功";
    /** 北森批量接口每批条数，避免单次请求过多超时或限流 */
    private static final int BATCH_SIZE = 70;
    /** 北森申请/职位/员工接口封装 */
    private final BeisenRecruitOwnerApiHelper apiHelper = new BeisenRecruitOwnerApiHelper();

    /**
     * OSF 入口：加载待同步单据，分批调用北森并回写招聘负责人。
     *
     * @param ctx BOS 上下文
     * @param map 入参，key 为 billIds（可选，逗号分隔内码）
     * @return code、message、successSize、errorSize、listMsg
     */
    @Override
    public Object process(Context ctx, Map<String, Object> map) throws EASBizException, BOSException {
        // billIds 为空=定时扫描，非空=界面指定单据
        String billIds = map.get("billIds") == null ? null : map.get("billIds").toString();
        logger.error("syncRecruitOwnerService billIds:" + billIds);

        Map<String, Object> result = new HashMap<String, Object>();
        List<Map<String, Object>> listMsg = new ArrayList<Map<String, Object>>();
        int successSize = 0;
        int errorSize = 0;

        IApplicantBeisen applicantBeisen = ApplicantBeisenFactory.getLocalInstance(ctx);
        ApplicantBeisenCollection collection = loadCollection(applicantBeisen, billIds);
        // 排除已有招聘负责人及历史数据，避免重复调北森
        List<ApplicantBeisenInfo> todoList = new ArrayList<ApplicantBeisenInfo>();
        for (int i = 0; i < collection.size(); i++) {
            ApplicantBeisenInfo info = collection.get(i);
            if (!shouldSkip(info)) {
                todoList.add(info);
            }
        }

        // 跨批次缓存：同一 originalId 只查一次员工接口
        Map<String, String> staffCodeCache = new HashMap<String, String>();
        Map<String, JSONObject> staffJsonCache = new HashMap<String, JSONObject>();
        for (int i = 0; i < todoList.size(); i += BATCH_SIZE) {
            int end = Math.min(i + BATCH_SIZE, todoList.size());
            List<ApplicantBeisenInfo> batch = todoList.subList(i, end);
            int[] counts = syncBatch(ctx, applicantBeisen, batch, staffCodeCache, staffJsonCache, listMsg);
            successSize += counts[0];
            errorSize += counts[1];
        }

        result.put("code", 200);
        result.put("message", "请求成功");
        result.put("successSize", successSize);
        result.put("errorSize", errorSize);
        result.put("listMsg", listMsg);
        return result;
    }

    /**
     * 处理一批单据：批量查申请与职位，再逐条解析人员并回写。
     *
     * @return int[2]，[0]成功条数，[1]失败条数
     */
    private int[] syncBatch(Context ctx, IApplicantBeisen applicantBeisen, List<ApplicantBeisenInfo> batch,
                            Map<String, String> staffCodeCache, Map<String, JSONObject> staffJsonCache,
                            List<Map<String, Object>> listMsg) {
        int successSize = 0;
        int errorSize = 0;
        Map<String, ApplicantBeisenInfo> applyInfoMap = new LinkedHashMap<String, ApplicantBeisenInfo>();
        List<String> applyIds = new ArrayList<String>();

        // 校验 applyId，无效直接回写失败
        for (ApplicantBeisenInfo info : batch) {
            String billId = info.getId().toString();
            String applyId = info.getApplyId();
            if (StringUtils.isEmpty(applyId)) {
                errorSize++;
                saveResultQuiet(applicantBeisen, info, "申请id为空");
                listMsg.add(buildMsg(billId, applyId, 500, "申请id为空"));
                continue;
            }
            applyIds.add(applyId);
            applyInfoMap.put(applyId, info);
        }
        if (applyIds.isEmpty()) {
            return new int[]{successSize, errorSize};
        }

        Map<String, String> applyIdToJobId = new HashMap<String, String>();
        JSONObject batchApplyJson = null;
        JSONObject batchJobJson = null;
        try {
            batchApplyJson = apiHelper.getApplyListByApplyIds(applyIds);
            if (batchApplyJson.getIntValue("code") != 200) {
                // 整批申请接口失败，本批统一记同一原因
                String msg = "根据申请ID获取申请信息失败:" + batchApplyJson.getString("message");
                for (String applyId : applyIds) {
                    ApplicantBeisenInfo info = applyInfoMap.get(applyId);
                    errorSize++;
                    saveResultQuiet(applicantBeisen, info, msg);
                    listMsg.add(buildMsg(info.getId().toString(), applyId, 500, msg, batchApplyJson));
                }
                return new int[]{successSize, errorSize};
            }
            JSONArray applyList = batchApplyJson.getJSONArray("data");
            if (applyList != null) {
                for (int j = 0; j < applyList.size(); j++) {
                    JSONObject item = applyList.getJSONObject(j);
                    applyIdToJobId.put(item.getString("applyId"), item.getString("jobId"));
                }
            }
        } catch (Exception e) {
            logger.error("批量获取申请信息失败", e);
            for (String applyId : applyIds) {
                ApplicantBeisenInfo info = applyInfoMap.get(applyId);
                errorSize++;
                saveResultQuiet(applicantBeisen, info, e.getMessage());
                listMsg.add(buildMsg(info.getId().toString(), applyId, 500, e.getMessage()));
            }
            return new int[]{successSize, errorSize};
        }

        Set<String> jobIdSet = new HashSet<String>(applyIdToJobId.values());
        jobIdSet.remove(null);
        Map<String, String> jobIdToHrDutyUser = new HashMap<String, String>();
        try {
            batchJobJson = batchLoadJobDutyUser(new ArrayList<String>(jobIdSet), jobIdToHrDutyUser);
        } catch (Exception e) {
            logger.error("批量获取职位信息失败", e);
            for (String applyId : applyIds) {
                ApplicantBeisenInfo info = applyInfoMap.get(applyId);
                errorSize++;
                saveResultQuiet(applicantBeisen, info, e.getMessage());
                listMsg.add(buildMsg(info.getId().toString(), applyId, 500, e.getMessage()));
            }
            return new int[]{successSize, errorSize};
        }

        // 逐条解析员工、匹配 sHR 人员并回写
        for (String applyId : applyIds) {
            ApplicantBeisenInfo info = applyInfoMap.get(applyId);
            String billId = info.getId().toString();
            try {
                Map<String, Object> syncResult = resolveAndSave(ctx, applicantBeisen, info, applyIdToJobId,
                        jobIdToHrDutyUser, staffCodeCache, staffJsonCache, batchApplyJson, batchJobJson);
                String msg = (String) syncResult.get("message");
                JSONObject apiData = (JSONObject) syncResult.get("apiData");
                if (SYNC_SUCCESS.equals(msg)) {
                    successSize++;
                    listMsg.add(buildMsg(billId, applyId, 200, msg, apiData));
                } else {
                    errorSize++;
                    listMsg.add(buildMsg(billId, applyId, 500, msg, apiData));
                }
            } catch (Exception e) {
                logger.error("同步招聘负责人失败, billId=" + billId, e);
                errorSize++;
                saveResultQuiet(applicantBeisen, info, e.getMessage());
                listMsg.add(buildMsg(billId, applyId, 500, e.getMessage()));
            }
        }
        return new int[]{successSize, errorSize};
    }

    /**
     * 批量查职位，填充 jobId → hrDutyUser（北森负责人 originalId）。
     */
    private JSONObject batchLoadJobDutyUser(List<String> jobIds, Map<String, String> jobIdToHrDutyUser) throws Exception {
        if (jobIds.isEmpty()) {
            return new JSONObject();
        }
        JSONObject jobJson = apiHelper.getJobListByIds(jobIds);
        if (jobJson.getIntValue("code") != 200) {
            throw new BOSException("根据职位Id获取职位列表失败:" + jobJson.getString("message"));
        }
        JSONArray jobList = jobJson.getJSONArray("data");
        if (jobList == null) {
            return jobJson;
        }
        for (int i = 0; i < jobList.size(); i++) {
            JSONObject item = jobList.getJSONObject(i);
            String jobId = item.getString("jobId");
            // 兼容 jobId 在 jobLite.jobGuid 中的返回结构
            if (StringUtils.isEmpty(jobId) && item.getJSONObject("jobLite") != null) {
                jobId = item.getJSONObject("jobLite").getString("jobGuid");
            }
            // 兜底：按请求顺序与入参对齐
            if (StringUtils.isEmpty(jobId) && i < jobIds.size()) {
                jobId = jobIds.get(i);
            }
            Object hrDutyUser = item.get("hrDutyUser");
            if (hrDutyUser != null) {
                jobIdToHrDutyUser.put(jobId, hrDutyUser.toString());
            }
        }
        return jobJson;
    }

    /**
     * 单条解析并回写：applyId → jobId → hrDutyUser → staffCode → sHR Person。
     *
     * @return message（同步结果）、apiData（北森中间数据快照）
     */
    private Map<String, Object> resolveAndSave(Context ctx, IApplicantBeisen applicantBeisen, ApplicantBeisenInfo info,
                                               Map<String, String> applyIdToJobId, Map<String, String> jobIdToHrDutyUser,
                                               Map<String, String> staffCodeCache, Map<String, JSONObject> staffJsonCache,
                                               JSONObject batchApplyJson, JSONObject batchJobJson) throws Exception {
        Map<String, Object> result = new HashMap<String, Object>();
        JSONObject apiData = new JSONObject();
        apiData.put("applyJson", batchApplyJson);
        apiData.put("jobJson", batchJobJson);

        String applyId = info.getApplyId();
        String jobId = applyIdToJobId.get(applyId);
        apiData.put("jobId", jobId);
        if (StringUtils.isEmpty(jobId)) {
            String msg = "根据申请ID未查询到申请信息或jobId为空";
            saveResult(applicantBeisen, info, msg);
            result.put("message", msg);
            result.put("apiData", apiData);
            return result;
        }

        String hrDutyUser = jobIdToHrDutyUser.get(jobId);
        apiData.put("hrDutyUser", hrDutyUser);
        if (StringUtils.isEmpty(hrDutyUser)) {
            String msg = "职位信息中hrDutyUser为空";
            saveResult(applicantBeisen, info, msg);
            result.put("message", msg);
            result.put("apiData", apiData);
            return result;
        }

        JSONObject staffJson = staffJsonCache.get(hrDutyUser);
        if (staffJson == null) {
            staffJson = apiHelper.getStaffByOriginalId(hrDutyUser);
            staffJsonCache.put(hrDutyUser, staffJson);
        }
        apiData.put("staffJson", staffJson);
        if (staffJson.getIntValue("code") != 200) {
            String msg = "获取员工信息失败:" + staffJson.getString("message");
            staffCodeCache.put(hrDutyUser, ""); // 标记已失败，避免同批重复请求
            saveResult(applicantBeisen, info, msg);
            result.put("message", msg);
            result.put("apiData", apiData);
            return result;
        }

        String staffCode = staffCodeCache.get(hrDutyUser);
        if (staffCode == null) {
            staffCode = BeisenRecruitOwnerApiHelper.parseStaffCode(staffJson);
            staffCodeCache.put(hrDutyUser, staffCode == null ? "" : staffCode);
        }
        apiData.put("staffCode", staffCode);
        if (StringUtils.isEmpty(staffCode)) {
            String msg = "员工信息中staffCode为空,originalId=" + hrDutyUser;
            saveResult(applicantBeisen, info, msg);
            result.put("message", msg);
            result.put("apiData", apiData);
            return result;
        }

        IPerson personIns = PersonFactory.getLocalInstance(ctx);
        PersonCollection persons = personIns.getPersonCollection("where number='" + staffCode + "'");
        if (persons.isEmpty()) {
            String msg = "sHR未找到工号为[" + staffCode + "]的人员";
            saveResult(applicantBeisen, info, msg);
            result.put("message", msg);
            result.put("apiData", apiData);
            return result;
        }
        info.put("recruitOwner", persons.get(0));
        saveResult(applicantBeisen, info, SYNC_SUCCESS);
        result.put("message", SYNC_SUCCESS);
        result.put("apiData", apiData);
        return result;
    }

    /** 按 billIds 或定时规则加载待同步集合 */
    private ApplicantBeisenCollection loadCollection(IApplicantBeisen applicantBeisen, String billIds)
            throws BOSException, EASBizException {
        FilterInfo filterInfo = buildFilterInfo(billIds);
        EntityViewInfo viewInfo = EntityViewInfo.getInstance(filterInfo, null, null);
        return applicantBeisen.getApplicantBeisenCollection(viewInfo);
    }

    /**
     * 构建查询条件：指定 billIds 时按 id 过滤；为空时扫 recruitOwner 为空且非历史数据。
     */
    private FilterInfo buildFilterInfo(String billIds) throws BOSException {
        FilterInfo filterInfo = new FilterInfo();
        FilterItemCollection filterItems = filterInfo.getFilterItems();
        if (!StringUtils.isEmpty(billIds)) {
            if (billIds.contains("'")) {
                billIds = billIds.replace("'", "");
            }
            filterItems.add(new FilterItemInfo("id", AtsUtil.toSet(billIds), CompareType.INCLUDE));
            return filterInfo;
        }
        try {
            // recruitOwner IS NULL：招聘负责人为空才纳入定时同步
            filterItems.add(new FilterItemInfo("recruitOwner", null, CompareType.EQUALS));
            // isHistoricalData IS NULL OR = 0：排除历史归档数据
            FilterInfo histOrFilter = new FilterInfo();
            histOrFilter.getFilterItems().add(new FilterItemInfo("isHistoricalData", null, CompareType.EQUALS));
            FilterInfo histZero = new FilterInfo();
            histZero.getFilterItems().add(new FilterItemInfo("isHistoricalData", Integer.valueOf(0), CompareType.EQUALS));
            histOrFilter.mergeFilter(histZero, "OR");
            filterInfo.mergeFilter(histOrFilter, "AND");
        } catch (Exception e) {
            throw new BOSException(e);
        }
        return filterInfo;
    }

    /** 已有招聘负责人或历史数据则跳过，避免重复同步 */
    private boolean shouldSkip(ApplicantBeisenInfo info) {
        if (!isRecruitOwnerEmpty(info)) {
            return true;
        }
        return isHistoricalData(info.get("isHistoricalData"));
    }

    /** 判断招聘负责人是否为空，为空表示待同步 */
    private boolean isRecruitOwnerEmpty(ApplicantBeisenInfo info) {
        return info.get("recruitOwner") == null;
    }

    /** 兼容 Boolean true 与 "1" 两种历史数据存储 */
    private boolean isHistoricalData(Object value) {
        if (value == null) {
            return false;
        }
        if (value instanceof Boolean) {
            return Boolean.TRUE.equals(value);
        }
        return "1".equals(String.valueOf(value));
    }

    /**
     * 回写 syncRecruit；成功时同时回写 recruitOwner。
     * 失败时不更新 recruitOwner，保留原值。
     */
    private void saveResult(IApplicantBeisen applicantBeisen, ApplicantBeisenInfo info, String message)
            throws BOSException, EASBizException {
        info.put("syncRecruit", message);
        SelectorItemCollection selector = new SelectorItemCollection();
        selector.add(new SelectorItemInfo("syncRecruit"));
        if (SYNC_SUCCESS.equals(message)) {
            selector.add(new SelectorItemInfo("recruitOwner"));
        }
        applicantBeisen.updatePartial(info, selector);
    }

    /** 静默回写，失败只打日志不中断整批 */
    private void saveResultQuiet(IApplicantBeisen applicantBeisen, ApplicantBeisenInfo info, String message) {
        try {
            saveResult(applicantBeisen, info, message);
        } catch (Exception e) {
            logger.error("回写syncRecruit失败, billId=" + info.getId(), e);
        }
    }

    /** 组装 OSF 逐条返回明细 */
    private Map<String, Object> buildMsg(String billId, String applyId, int code, String message) {
        return buildMsg(billId, applyId, code, message, null);
    }

    /** 组装 OSF 逐条返回明细，可附带北森 apiData */
    private Map<String, Object> buildMsg(String billId, String applyId, int code, String message, JSONObject apiData) {
        Map<String, Object> item = new HashMap<String, Object>();
        item.put("billId", billId);
        item.put("applyId", applyId);
        item.put("code", code);
        item.put("message", message);
        if (apiData != null) {
            item.put("apiData", apiData);
        }
        return item;
    }
}
