package nckd.jimin.jyyy.hr.wtc.wtis.util;


import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.utils.ObjectUtils;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.wtc.wtbs.common.util.WTCDateUtils;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Map;

/**
 * description: 同步钉钉通用帮助类、包含休假、出差、补签
 * 分三种情况：新增，变更，撤销
 * 1、正常通知审批通过，单据审批通过后调用钉钉审批通过接口
 * 2、星瀚单据变更后根据ApproveId调用钉钉接口更新，补卡没有变更
 * 3、星瀚单据变更失效后，调用钉钉审批撤销接口，请假单判断是否失效标识：isnotleave，出差单判断是否失效标识：isnottrip，
 * @author Tyx
 * @date  2025/5/29
 */
public class SyncAttendanceHelper {

    private static Log log = LogFactory.getLog(SyncAttendanceHelper.class);
    private static String baseUrl = "";
    private static String finishParamKey = "getFinishUrl";
    private static String cancelParamKey = "getCancelUrl";
    private static String fillParamKey = "getFillUrl";
    private static String shiftParamKey = "getShiftUrl";
    private static String billNo = "";
    private static String accessToken = "";

    /**
     * 统一入口
     * @param bill         单据Dyo
     * @param entityNumber 单据标识
     * @param bizType      类型 1：加班，2：出差，3：请假，4：补卡
     * @param isChange     是否变更
     */
    public static void execute (DynamicObject bill, String entityNumber, String bizType, boolean isChange) {
        billNo = bill.getString("billno");
        //用户映射Map key = 人员工号, value = openId
        Map<String, String> openMap = DingTalkSyncUtil.convertMappingInfo();
        //获取accessToken
        try {
            //重新查一遍单据 避免部分属性没有
            bill = BusinessDataServiceHelper.loadSingle(bill.getPkValue(), entityNumber);
            //获取AccessToken
            accessToken = DingTalkSyncUtil.getAccessToken();
            log.info("钉钉单据同步实际调用accessToken : {}", accessToken);
            //构建参数
            JSONObject param = buildContent(bill, entityNumber, bizType, isChange, openMap);
            log.info("钉钉单据同步实际调用入参报文 : {}", param.toJSONString());
            //获取调用url
            String url = getUrl(baseUrl, accessToken);
            log.info("钉钉单据同步实际调用url : {}", url);
            //实际调用
            JSONObject reponseObject = doPost(url, param);
            log.info("钉钉单据同步实际返回信息 : {}", reponseObject.toJSONString());
            //保存日志 errcode == "0"为成功
            if("0".equals(reponseObject.getString("errcode"))) {
                DingTalkSyncUtil.createLog("A", billNo, param.toJSONString(),reponseObject.toJSONString(),DingTalkSyncUtil.typeMap.get(bizType));
            }
            else {
                DingTalkSyncUtil.createLog("C", billNo, param.toJSONString(),reponseObject.toJSONString(),DingTalkSyncUtil.typeMap.get(bizType));
            }
        } catch (Exception e) {
            DingTalkSyncUtil.createLog("C", billNo, bill.getString("billno") + "调用失败", e.getMessage(), DingTalkSyncUtil.typeMap.get(bizType));
            throw new RuntimeException(e);
        }
    }

    /**
     * 实际调用
     * @param url
     * @param bodyData
     * @return
     * @throws IOException
     */
    public static JSONObject doPost (String url, JSONObject bodyData) throws IOException {
        return DingTalkSyncUtil.doPostByHttpClient(url, bodyData);
    }

    /**
     * 构建入参
     * @param bill         单据Dyo
     * @param entityNumber 单据标识
     * @param bizType      类型 1：加班，2：出差，3：请假
     * @param isChange     是否变更
     * @return
     */
    public static JSONObject buildContent(DynamicObject bill, String entityNumber, String bizType, boolean isChange, Map openMap) throws IOException {
        JSONObject param = new JSONObject();
        switch(bizType) {
            case "2" :
                buildTripContent(param, bill, entityNumber, isChange, openMap);
                break;
            case "3" :
                buildVAContent(param, bill, entityNumber, isChange, openMap);
                break;
            case "4" :
                buildFillContent(param, bill, entityNumber, isChange, openMap);
                break;
        }
        return param;
    }

    /**
     * 构建出差入参
     * @param param
     * @param bill
     * @param entityNumber
     * @param isChange
     * @param openMap
     */
    private static void buildTripContent(JSONObject param, DynamicObject bill, String entityNumber, boolean isChange, Map openMap) {
        log.info("构建出差入参报文 start ");
        boolean isCancel = bill.getBoolean("isnottrip");
        getBaseUrl(isCancel, false);
        //判断是否出差失效
        if(isCancel) {
            param.put("approve_id", bill.getLong("originalid"));
            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
        }
        else {
            param.put("biz_type", 2);
            param.put("jump_url", "https://open.dingtalk.com/");
            param.put("tag_name", "出差");
            param.put("calculate_model", "1");
            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
            DynamicObject entry = bill.getDynamicObjectCollection("entryentity").get(0);
            //sub_type 请假类型
            param.put("sub_type", entry.getString("busitriptype.name"));
            //approve_id 直接取原单ID
            param.put("approve_id", bill.getLong("originalid"));
            //分录单位 0-上半天 1-下半天 2-全天 3-时分
            String startMethod = entry.getString("startmethod");
            String endMethod = entry.getString("endmethod");
            //duration_unit 单位 day-全天 halfDay-半天 hour-时分
            String unitStr = "";
            switch (startMethod) {
                case "0":
                case "1":
                    unitStr = "halfDay";
                    break;
                case "2":
                    unitStr = "day";
                    break;
                case "3":
                    unitStr = "hour";
                    break;
            }
            param.put("duration_unit", unitStr);
            //根据单位处理时长
            param.put("from_time", dealHalfDaySdf(entry.getDate("startdate"), startMethod));
            param.put("to_time", dealHalfDaySdf(entry.getDate("enddate"), endMethod));
        }
    }

    /**
     * 构建休假入参
     * @param param
     * @param bill
     * @param entityNumber
     * @param isChange
     * @param openMap
     */
    private static void buildVAContent(JSONObject param, DynamicObject bill, String entityNumber, boolean isChange, Map openMap) {
        log.info("构建休假入参报文 start ");
        boolean isCancel = bill.getBoolean("isnotleave");
        getBaseUrl(isCancel, false);
        //休假失效的情况：
        if(isCancel) {
            param.put("approve_id", bill.getLong("originalid"));
            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
        }
        //非失效的情况：
        else {
            param.put("biz_type", 3);
            param.put("jump_url", "https://open.dingtalk.com/");
            param.put("tag_name", "请假");
            param.put("calculate_model", "1");
            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
            DynamicObject entry = bill.getDynamicObjectCollection("entryentity").get(0);
            //sub_type 请假类型
            param.put("sub_type", entry.getString("entryvacationtype.name"));
            //approve_id 直接取原单ID
            param.put("approve_id", bill.getLong("originalid"));
            //分录单位 0-上半天 1-下半天 2-全天 3-时分
            String startMethod = entry.getString("entrystartmethod");
            String endMethod = entry.getString("entryendmethod");
            //duration_unit 单位 day-全天 halfDay-半天 hour-时分
            String unitStr = "";
            switch (startMethod) {
                case "0":
                case "1":
                    unitStr = "halfDay";
                    break;
                case "2":
                    unitStr = "day";
                    break;
                case "3":
                    unitStr = "hour";
                    break;
            }
            param.put("duration_unit", unitStr);
            //根据单位处理时长
            param.put("from_time", dealHalfDaySdf(entry.getDate("entrystartdate"), startMethod));
            param.put("to_time", dealHalfDaySdf(entry.getDate("entryenddate"), endMethod));
        }
    }

    /**
     * 构建补卡入参
     * @param param
     * @param bill
     * @param entityNumber
     * @param isChange
     * @param openMap
     */
    private static void buildFillContent(JSONObject param, DynamicObject bill, String entityNumber, boolean isChange, Map openMap) throws IOException {
        log.info("构建补卡入参报文 start ");
        getBaseUrl(isChange, true);
        //补卡没有变更，isChange=true直接判断为失效
        if(isChange) {
            param.put("approve_id", bill.getLong("originalid"));
            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
        }
        else {
            param.put("jump_url", "https://open.dingtalk.com/");
            param.put("tag_name", "补卡");
            param.put("approve_id", bill.getLong("originalid"));
            param.put("userid", openMap.get(bill.getString("attfile.personnum")));
            DynamicObject entry = bill.getDynamicObjectCollection("entryentity").get(0);
            //work_date
            Date signDate = entry.getDate("signdate");
            param.put("work_date", WTCDateUtils.date2Str(signDate, "yyyy-MM-dd"));
            long time = entry.getLong("suppleworktime");
            LocalTime localTime = WTCDateUtils.secondToTime(time);
            LocalDateTime localDateTime = LocalDateTime.of(WTCDateUtils.toLocalDate(signDate), localTime);
            String dateTimeStr = localDateTimeToStr(localDateTime, "yyyy-MM-dd HH:mm:ss");
            param.put("punch_check_time",dateTimeStr);
            param.put("user_check_time",dateTimeStr);
            //punch_id 钉钉排班ID 另外调接口获取
            param.put("punch_id",getDingTalkShiftId(bill, openMap));
        }
    }


    /**
     * 返回Url
     * @param isCancel
     * @param isFill
     */
   public static void getBaseUrl (boolean isCancel, boolean isFill) {
        if(isCancel) {
            baseUrl = DingTalkSyncUtil.getParamValue(cancelParamKey);
        }
        else if(isFill) {
            baseUrl = DingTalkSyncUtil.getParamValue(fillParamKey);
        }
        else {
            baseUrl = DingTalkSyncUtil.getParamValue(finishParamKey);
        }
   }

    /**
     * 获取钉钉班次ID
     * {
     * 	"errcode": 0,
     * 	"errmsg": "ok",
     * 	"result": [
     *                {
     * 			"check_type": "OnDuty",
     * 			"group_id": 1250482934,
     * 			"id": 814088651114,
     * 			"is_rest": "N",
     * 			"overtime_setting_id": 696580239,
     * 			"plan_check_time": "2025-06-03 08:30:00",
     * 			"shift_id": 1386234138,
     * 			"shift_version": 1317657354,
     * 			"userid": "13697915049",
     * 			"work_date": "2025-06-03 00:00:00"
     *        },
     *        {
     * 			"check_type": "OffDuty",
     * 			"group_id": 1250482934,
     * 			"id": 814088651115,
     * 			"is_rest": "N",
     * 			"overtime_setting_id": 696580239,
     * 			"plan_check_time": "2025-06-03 17:30:00",
     * 			"shift_id": 1386234138,
     * 			"shift_version": 1317657354,
     * 			"userid": "13697915049",
     * 			"work_date": "2025-06-03 00:00:00"
     *        }
     * 	],
     * 	"success": true,
     * 	"request_id": "16kk36k9q33hf"
     * }
     * @return
     */
    public static Long getDingTalkShiftId (DynamicObject bill, Map openMap) throws IOException {
        DynamicObject entry = bill.getDynamicObjectCollection("entryentity").get(0);
        Date shiftDate = entry.getDate("signdate");
        Long shiftDateUnix = dateToUnix(shiftDate);
        JSONObject ob = new JSONObject(true);
        ob.put("from_date_time", shiftDateUnix);
        ob.put("to_date_time", shiftDateUnix);
        ob.put("op_user_id", "000082");
        ob.put("userids", openMap.get(bill.getString("attfile.personnum")));
        String url = DingTalkSyncUtil.getParamValue(shiftParamKey);
        url = getUrl(url, accessToken);
        JSONObject response = doPost(url, ob);
        log.info("调用排班返回：{}", response);
        DingTalkSyncUtil.createLog("A", bill.getString("billno"), ob.toJSONString(), response.toJSONString(), DingTalkSyncUtil.SyncShift);
        if(ObjectUtils.isEmpty(response)) {
            return null;
        }
        JSONArray shiftArr = response.getJSONArray("result");
        if(ObjectUtils.isEmpty(shiftArr)) {
            return null;
        }
        //根据进出卡标识获取对应标识
        String tag = entry.getString("accesstag");
        String checkType = "on".equals(tag) ? "OnDuty" : "OffDuty";

        for(int i = 0 ; i < shiftArr.size(); i++) {
            JSONObject shift = shiftArr.getJSONObject(i);
            if(!checkType.equals(shift.getString("check_type")))
                continue;
            return shift.getLong("id");
        }

        return null;
    }


    public static String getUrl (String url, String accessToken) {
        return url + "?access_token=" + accessToken;
    }

    /**
     * 根据单位处理时间格式
     * method == 0 || method == 1时：返回yyyy-MM-dd AM 或者 yyyy-MM-dd PM
     * method == 2 返回yyyy-MM-dd
     * method == 3 返回yyyy-MM-dd HH:mm:ss
     * @param date
     * @param method
     * @return
     */
    public static String dealHalfDaySdf (Date date, String method) {
        SimpleDateFormat sdf;
        switch(method) {
            case "0":
                sdf = new SimpleDateFormat("yyyy-MM-dd");
                return sdf.format(date) + " AM";
            case "1":
                sdf = new SimpleDateFormat("yyyy-MM-dd");
                return sdf.format(date) + " PM";
            case "2":
                sdf = new SimpleDateFormat("yyyy-MM-dd");
                return sdf.format(date);
            case "3":
                sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                return sdf.format(date);
            default :
                return null;
        }
    }

    /**
     * localDateTime转String
     * @param localDateTime
     * @param pattern
     * @return
     */
    public static String localDateTimeToStr (LocalDateTime localDateTime, String pattern) {
        DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern(pattern);
        return localDateTime.format(dateFormat);
    }

    /**
     * date转Unix
     * @param date
     * @return
     */
    public static Long dateToUnix (Date date) {
        return date.getTime();
    }


}
