package com.malk.zyjn.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.google.common.base.Strings;
import com.malk.server.aliwork.YDConf;
import com.malk.server.aliwork.YDParam;
import com.malk.server.common.McR;
import com.malk.server.dingtalk.DDConf;
import com.malk.server.dingtalk.DDR_New;
import com.malk.service.aliwork.YDClient;
import com.malk.service.dingtalk.DDClient;
import com.malk.utils.UtilHttp;
import com.malk.utils.UtilMap;
import com.malk.zyjn.entity.Attendance;
import com.malk.zyjn.service.ZyjnService;
import com.malk.zyjn.utils.UtilExcel;
import lombok.extern.slf4j.Slf4j;
import okio.BufferedSink;
import okio.Okio;
import okio.Source;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Slf4j
@Service
public class ZyjnServiceImpl implements ZyjnService {
    @Value(value = "${tableFile.path}")
    private String tableFilePath;

    @Value(value = "${download.url}")
    private String downloadUrl;

    @Autowired
    private DDClient ddClient;

    @Autowired
    private DDConf ddConf;

    @Autowired
    private YDClient ydClient;

    @Autowired
    private YDConf ydConf;

    private static final String COLUM_ATTENDANCE_RESULTS = "995157155";//考勤结果列id

    private static final String SPACE_ID = "24768523387";//钉盘考勤报表空间id

    private static final String ADMIN_UNION_ID = "oqzo73xXs9EdIiSatosDYqgiEiE";//管理员-任欢欢union_id

    private static final String DEPT_ID = "161860966";//综合办公室部门id


    @Override
    public McR getDeptAttendanceSheet(String userId, String userName,String deptId, Long date, String deptName) {
        //获取发起人所属最上层部门
        Map<String,Object> params = new HashMap<>();
        params.put("access_token",ddClient.getAccessToken());
        Map<String,Object> body = new HashMap<>();
        body.put("dept_id",deptId);
        DDR_New deptDDR = (DDR_New) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/v2/department/listparentbydept", null, params, body, DDR_New.class);
        Map deptDDRResult = (Map) deptDDR.getResult();
        List<String> parentDeptIdList = (List<String>) deptDDRResult.get("parent_id_list");
        String topDeptId = deptId;
        if (parentDeptIdList.size() >= 2){
            topDeptId = String.valueOf(parentDeptIdList.get(parentDeptIdList.size()-2));
        }

        //获取发起人所属最上层部门下所有成员
        Map body2 = new HashMap();
        body2.put("dept_id",topDeptId);
        body2.put("cursor",0);
        body2.put("size",100);

        DDR_New employeeDDR = (DDR_New) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/user/listsimple", null, params, body2, DDR_New.class);
        Map employeeResult = (Map) employeeDDR.getResult();
        List<Map> userList = (List<Map>) employeeResult.get("list");

        //遍历获取每个成员考勤记录
        List<Attendance> employeeAttendanceList = new ArrayList<>();
        if (!userList.isEmpty()){
            DateTime startDate = DateUtil.date(date);
            DateTime endDate = DateUtil.endOfMonth(startDate);

            int id = 1;
            for (Map user : userList) {
                //查询用户当月请假情况
                Map<String,Object> param3 = new HashMap<>();
                param3.put("access_token",ddClient.getAccessToken());
                Map body3 = new HashMap<>();
                body3.put("userid_list",userId);
                body3.put("start_time",startDate.getTime());
                body3.put("end_time",endDate.getTime());
                body3.put("offset",0);
                body3.put("size",20);

                DDR_New ddrNew3 = (DDR_New) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/attendance/getleavestatus", null, param3, body3, DDR_New.class);
                Map result3 = (Map) ddrNew3.getResult();
                List<Map> leaveStatus = (List<Map>) result3.get("leave_status");

                int index = 1;
                String userid = user.get("userid").toString();
                String name = user.get("name").toString();
                Map map = new HashMap();
                map.put("column_id_list",COLUM_ATTENDANCE_RESULTS);
                map.put("from_date",startDate.toString());
                map.put("to_date",endDate.toString());
                map.put("userid",userid);
                DDR_New attendanceDDR = (DDR_New) UtilHttp.doPost("https://oapi.dingtalk.com/topapi/attendance/getcolumnval", null, params, map, DDR_New.class);
                Map attendanceResult = (Map) attendanceDDR.getResult();
                Map columnVals = ((List<Map>) attendanceResult.get("column_vals")).get(0);
                List<Map> attendanceList = (List<Map>) columnVals.get("column_vals");
                Attendance attendance = new Attendance(id,name);
                /*List<Map> filterAttendanceList = attendanceList.stream()
                        .filter(item -> DateUtil.parse(item.get("date").toString()).isBefore(DateTime.now()))
                        .collect(Collectors.toList());*/
                for (Map attendanceMap : attendanceList) {
                    String situation = attendanceMap.get("value").toString();
                    if (situation.contains("休息")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"假");
                    }else if (situation.contains("旷工")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"工");
                        if (!leaveStatus.isEmpty()){
                            long time = DateUtil.parse(attendanceMap.get("date").toString()).getTime();
                            for (Map leaveState : leaveStatus) {
                                long startTime = DateUtil.beginOfDay(new Date((long) leaveState.get("start_time"))).getTime();
                                long endTime = DateUtil.endOfDay(new Date((long) leaveState.get("end_time"))).getTime();
                                if (time >= startTime && time <= endTime){
                                    //请假未审批
                                    ReflectUtil.invoke(attendance,"setSituation"+index,"?");
                                }
                            }
                        }
                    }else if (situation.contains("公休假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"公");
                    }else if (situation.contains("调休")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"调");
                    }else if (situation.contains("病假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"病");
                    }else if (situation.contains("事假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"事");
                    }else if (situation.contains("丧假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"丧");
                    }else if (situation.contains("婚假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"婚");
                    }else if (situation.contains("产假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"产");
                    }else if (situation.contains("探亲假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"探");
                    }else if (situation.contains("哺乳假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"哺");
                    }else if (situation.contains("产前假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"前");
                    }else if (situation.contains("育儿假")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"育");
                    }else if (situation.contains("未打卡")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"工");
                        if (!leaveStatus.isEmpty()){
                            long time = DateUtil.parse(attendanceMap.get("date").toString()).getTime();
                            for (Map leaveState : leaveStatus) {
                                long startTime = DateUtil.beginOfDay(new Date((long) leaveState.get("start_time"))).getTime();
                                long endTime = DateUtil.endOfDay(new Date((long) leaveState.get("end_time"))).getTime();
                                if (time >= startTime && time <= endTime){
                                    //请假未审批
                                    ReflectUtil.invoke(attendance,"setSituation"+index,"?");
                                }
                            }
                        }
                    }else if (situation.contains("正常")){
                        ReflectUtil.invoke(attendance,"setSituation"+index,"工");
                    }else {
                        ReflectUtil.invoke(attendance,"setSituation"+index,"工");
                    }
                    index++;
                }
                employeeAttendanceList.add(attendance);
                id++;
            }
        }

        //生成报表
        Map mainData = new HashMap();
        mainData.put("department",deptName);
        mainData.put("date",DateUtil.format(DateTime.of(date),"yyyy年MM月"));
        mainData.put("subName",userName);

        UtilExcel.exportAllSheetByTemplate(mainData,employeeAttendanceList,deptName+mainData.get("date")+"考勤表(未审核)",tableFilePath,"template.xlsx");

        //上传至钉盘
        String fileName = deptName + mainData.get("date") + "考勤表(未审核).xlsx";
        uploadToDingTalk(tableFilePath + fileName,userId);

        Map result = new HashMap<>();
        result.put("downloadUrl",downloadUrl + fileName);
        result.put("fileName",fileName);
        result.put("ext","xlsx");

        //返回下载文件url
        return McR.success(result);
    }



    @Override
    public void download(String fileName, HttpServletResponse response) {
        File file = new File(tableFilePath + fileName);
        if (!file.exists()) {
            return; //文件不存在
        }

        try {
            fileName = URLEncoder.encode(file.getName(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        response.setHeader("Content-disposition", "attachment;filename="+fileName+";"+"filename*=utf-8''"+fileName);
        response.setContentType("application/octet-stream");

        try (BufferedSink sink = Okio.buffer(Okio.sink(response.getOutputStream()))) {
            Source source = Okio.source(file);
            sink.writeAll(source);
            source.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void downloadFile(String fileURL, String savePath) throws IOException {
        //如果fileUrl中包含中文，则需要对其进行编码
        Pattern pattern = Pattern.compile("[\u4E00-\u9FA5]");
        Matcher matcher = pattern.matcher(fileURL);
        if (matcher.find()){
            String encodeFileName = URLEncoder.encode(fileURL.substring(fileURL.lastIndexOf("/") + 1), "UTF-8");
            fileURL = fileURL.substring(0,fileURL.lastIndexOf("/") + 1) + encodeFileName;
        }
        //若是宜搭附件url 则需获得附件临时免登地址
        if (fileURL.contains("aliwork")){
            Map<String,Object> param = new HashMap<>();
            param.put("systemToken",ydConf.getSystemToken());
            param.put("userId","253434204020308091");//任欢欢
            param.put("fileUrl",fileURL);

            fileURL = ((DDR_New) UtilHttp.doGet("https://api.dingtalk.com/v1.0/yida/apps/temporaryUrls/" + ydConf.getAppType(), ddClient.initTokenHeader(), param, DDR_New.class)).getResult().toString();
        }

        URL url = new URL(fileURL);
        HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
        int responseCode = httpConn.getResponseCode();

        // 检查HTTP响应代码是否为200
        if (responseCode == HttpURLConnection.HTTP_OK) {
            InputStream inputStream = httpConn.getInputStream();
            FileOutputStream outputStream = new FileOutputStream(savePath);

            byte[] buffer = new byte[4096];
            int bytesRead = -1;

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            outputStream.close();
            inputStream.close();
        } else {
            System.out.println("无法下载文件。HTTP响应代码: " + responseCode);
        }
        httpConn.disconnect();
    }

    @Async
    @Override
    public void updateDeptAttendanceSheet(String formInstId) {
        //获取文件下载链接
        DDR_New ddrNew = ydClient.queryData(YDParam.builder()
                .formInstId(formInstId)
                .build(), YDConf.FORM_QUERY.retrieve_id);
        Map formData = ddrNew.getFormData();
        String userId = ddrNew.getOriginator().get("userId").toString();
        List<Map> fileList = (List<Map>) JSONArray.parse(String.valueOf(formData.get("attachmentField_lzkvrlcl")));
        String ydDownloadUrl = fileList.get(0).get("downloadUrl").toString();
        String fileName = fileList.get(0).get("name").toString();
        //将downloadurl下载到本地
        try {
            downloadFile(ydDownloadUrl, tableFilePath + fileName);
            System.out.println("文件下载成功！");
        } catch (IOException e) {
            e.printStackTrace();
        }

        //获取审批记录
        Map param = new HashMap<>();
        param.put("appType",ydConf.getAppType());
        param.put("systemToken",ydConf.getSystemToken());
        param.put("userId",userId);
        param.put("processInstanceId",formInstId);

        DDR_New ddrNew2 = (DDR_New) UtilHttp.doGet("https://api.dingtalk.com/v1.0/yida/processes/operationRecords", ddClient.initTokenHeader(), param, DDR_New.class);
        List<Map> result = (List<Map>) ddrNew2.getResult();

        String startTime = result.get(0).get("operateTimeGMT").toString().replace("T"," ").replace("Z","");
        String endTime = result.get(result.size() - 2).get("operateTimeGMT").toString().replace("T", " ").replace("Z", "");
        String operatorName = result.get(result.size() - 2).get("operatorName").toString();

        //更新部门考勤表审批人信息
        try {
            Workbook workbook = WorkbookFactory.create(new FileInputStream(tableFilePath + fileName));
            Sheet sheet = workbook.getSheetAt(0);
            List<Integer> rowsToBeRemoved = new ArrayList<>();
            for (int i = 0; i <= sheet.getLastRowNum(); i++) {
                Row row = sheet.getRow(i);

                if (isRowEmpty(row)) {
                    rowsToBeRemoved.add(i);
                    continue;
                }
                for (int j = 0; j <= row.getLastCellNum(); j++) {
                    Cell cell = row.getCell(j);
                    if (Objects.nonNull(cell)){
                        if (cell.getCellTypeEnum() == CellType.STRING){
                            String cellValue = cell.getStringCellValue();
                            if (cellValue.equals("部门考勤员")){
                                Cell cell1 = row.createCell(29);
                                cell1.setCellValue("提交");
                                Cell cell2 = row.createCell(31);
                                cell2.setCellValue(startTime);
                                break;
                            }else if (cellValue.equals("部门负责人")){
                                Cell cell3 = row.createCell(27);
                                cell3.setCellValue(operatorName);
                                Cell cell4 = row.createCell(29);
                                cell4.setCellValue("审核通过");
                                Cell cell5 = row.createCell(31);
                                cell5.setCellValue(endTime);
                                break;
                            }
                        }
                    }
                }

            }

            //删除空行
            rowsToBeRemoved.remove(0);
            rowsToBeRemoved.remove(rowsToBeRemoved.size() - 1);
            for (int rowNum : rowsToBeRemoved) {
                sheet.shiftRows(rowsToBeRemoved.get(0) + 1, sheet.getLastRowNum(), -1);
            }

            // 保存修改后的Excel文件
            FileOutputStream fileOut = new FileOutputStream(tableFilePath + fileName.replace("(未审核)",""));
            workbook.write(fileOut);
            fileOut.close();

            workbook.close();
        } catch (IOException | EncryptedDocumentException e) {
            e.printStackTrace();
        } catch (InvalidFormatException e) {
            throw new RuntimeException(e);
        }

        //更新宜搭考勤表
        Map attachment = new HashMap();
        String newDownloadUrl = downloadUrl + fileName.replace("(未审核)", "");
        attachment.put("downloadUrl", newDownloadUrl);
        attachment.put("name",fileName.replace("(未审核)",""));
        attachment.put("previewUrl", newDownloadUrl);
        attachment.put("url",newDownloadUrl);
        Map map = new HashMap();
        map.put("attachmentField_lzkvrlcl",new Map[]{attachment});

        ydClient.operateData(YDParam.builder()
                .formInstId(formInstId)
                .updateFormDataJson(JSONArray.toJSONString(map))
                .build(), YDConf.FORM_OPERATION.update);

        //上传钉盘
        Map dentry = uploadToDingTalk(tableFilePath + fileName.replace("(未审核)",""), userId);
        String dentryUuid = dentry.get("uuid").toString();

        //设置权限 发起人及综办可下载
        Map<String,Object> param3 = new HashMap<>();
        param3.put("unionId",ADMIN_UNION_ID);//管理员-任欢欢

        List<Map> dentryPermissionList = new ArrayList<>();
        dentryPermissionList.add(new HashMap<String,Object>(){{
            put("type","USER");
            put("id",userId);//发起人userid
        }});
        dentryPermissionList.add(new HashMap<String,Object>(){{
            put("type","DEPT");
            put("id",DEPT_ID);//综办部门id
        }});
        Map body3 = new HashMap();
        body3.put("roleId","DOWNLOADER");//查看下载者
        body3.put("members",dentryPermissionList);

        DDR_New ddrNew3 = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v2.0/storage/spaces/dentries/" + dentryUuid + "/permissions", ddClient.initTokenHeader(), param3, body3, DDR_New.class);
        if (ddrNew3.isSuccess()){
            log.info("权限设置成功!");
        }else {
            log.error("权限设置失败！" + ddrNew3.getCode() + " " + ddrNew3.getMessage());
        }

        //查询未审核考勤表
        Map<String,Object> param4 = new HashMap<>();
        param4.put("unionId",ADMIN_UNION_ID);//管理员-任欢欢
        param4.put("parentId",0);
        DDR_New ddrNew4 = (DDR_New) UtilHttp.doGet("https://api.dingtalk.com/v1.0/storage/spaces/"+SPACE_ID+"/dentries", ddClient.initTokenHeader(), param4, DDR_New.class);
        List<Map> dentries = ddrNew4.getDentries();
        for (Map dentry2 : dentries) {
            if (dentry2.get("name").toString().equals(fileName)) {
                String dentryId = dentry2.get("id").toString();
                //删除未审核考勤表
                Map param5 = new HashMap();
                param5.put("unionId",ADMIN_UNION_ID);//管理员-任欢欢
                UtilHttp.doDelete("https://api.dingtalk.com/v1.0/storage/spaces/"+SPACE_ID+"/dentries/"+dentryId,ddClient.initTokenHeader(),param5, DDR_New.class);
                break;
            }
        }


    }

    private static boolean isRowEmpty(Row row) {
        if (row == null) return true;
        for (int c = row.getFirstCellNum(); c < row.getLastCellNum(); c++) {
            Cell cell = row.getCell(c);

            if (cell != null){
                CellType cellTypeEnum = cell.getCellTypeEnum();

                if (cellTypeEnum == CellType.STRING){
                    String stringCellValue = cell.getStringCellValue();
                    if (!stringCellValue.isEmpty()){
                        return false;
                    }
                } else if (cellTypeEnum != CellType.BLANK){
                    return false;
                }
            }
        }
        return true;
    }

    private Map uploadToDingTalk(String filePath, String userId) {
        //获取当前时间戳
        long beginTimestamp = System.currentTimeMillis();
        System.out.println("开始上传部门考勤表：" + DateUtil.format(new Date(beginTimestamp), "yyyy-MM-dd HH:mm:ss"));

        //文件
        File file = new File(filePath);

        //获取文件上传信息
        String spaceId = SPACE_ID;//考勤报表-空间id
        Map param = new HashMap();
        param.put("unionId",ADMIN_UNION_ID);//任欢欢
        Map body = new HashMap();
        body.put("protocol","HEADER_SIGNATURE");
        body.put("multipart",false);

        DDR_New ddr = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v1.0/storage/spaces/" + spaceId + "/files/uploadInfos/query", ddConf.initTokenHeader(ddClient.getAccessToken()), param, body, DDR_New.class);
        String uploadKey = ddr.getUploadKey();
        Map headerSignatureInfo = ddr.getHeaderSignatureInfo();
        Map<String,String> headers = (Map<String,String>) headerSignatureInfo.get("headers");
        List<String> resourceUrls = (List<String>) headerSignatureInfo.get("resourceUrls");
        String resourceUrl = resourceUrls.get(0);

        Map dentry = new HashMap();
        //使用OSS的header加签方式上传文件
        try {
            URL url = new URL(resourceUrl);
            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            if (headers != null) {
                for (Map.Entry<String, String> entry : headers.entrySet()) {
                    connection.setRequestProperty(entry.getKey(), entry.getValue());
                }
            }
            connection.setDoOutput(true);
            connection.setRequestMethod("PUT");
            connection.setUseCaches(false);
            connection.setReadTimeout(10000);
            connection.setConnectTimeout(10000);
            connection.connect();
            OutputStream out = connection.getOutputStream();
            InputStream is = new FileInputStream(file);
            byte[] b =new byte[1024];
            int temp;
            while ((temp=is.read(b))!=-1){
                out.write(b,0,temp);
            }
            out.flush();
            out.close();
            int responseCode = connection.getResponseCode();
            connection.disconnect();
            if (responseCode == 200) {
                System.out.println("上传成功");
            } else {
                System.out.println("上传失败");
            }
            //提交文件
            Map body2 = new HashMap();
            Map option = new HashMap();
            option.put("conflictStrategy","OVERWRITE");
            body2.put("uploadKey",uploadKey);
            body2.put("name",file.getName());
            body2.put("parentId","0");
            body2.put("option",option);
            DDR_New ddrNew = (DDR_New) UtilHttp.doPost("https://api.dingtalk.com/v1.0/storage/spaces/" + spaceId + "/files/commit", ddClient.initTokenHeader(), param, body2, DDR_New.class);
            dentry = ddrNew.getDentry();

        }catch (IOException e){
            log.info("上传文件异常:{}",e);
        }

        long endTimestamp = System.currentTimeMillis();
        System.out.println("上传文件结束：" + DateUtil.format(new Date(endTimestamp), "yyyy-MM-dd HH:mm:ss"));
        System.out.println("上传文件耗时：" + (endTimestamp - beginTimestamp)/1000.0 + "s");
        return dentry;
    }
}
