package thyyxxk.webserver.service.archive;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.format.FastDateFormat;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dtflys.forest.Forest;
import com.dtflys.forest.http.ForestRequest;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import thyyxxk.webserver.config.envionment.ArchiveConfig;
import thyyxxk.webserver.config.exception.BizException;
import thyyxxk.webserver.config.exception.ExceptionEnum;
import thyyxxk.webserver.dao.his.archive.PatientArchiveDao;
import thyyxxk.webserver.entity.ResultVo;
import thyyxxk.webserver.entity.archive.PatientArchive;
import thyyxxk.webserver.http.websocket.dto.ArchiveSocketParam;
import thyyxxk.webserver.http.websocket.SocketV2;
import thyyxxk.webserver.service.hutoolcache.UserCache;
import thyyxxk.webserver.utils.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
public class ArchiveServer {
    private final ArchiveConfig archiveData;
    private final PatientArchiveDao patientArchiveDao;
    private final SocketV2 socketV2;
    private final UserCache userCache;

    private static final String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdTugOj9/hmOJIeXB7BOVKlczqjhXp6s7cvXoSrxhllgApZOivyLaq/4hsCw0m+70xj0GNIMIy8DFKKhz5z0DdOdPwoUwIbzmH8VZfgSM3eridfYJ8wnP3XOLlz3daX5jwD2rcNAMGar9vbi4k7gnkbH7vlRpujdeJyhqcCONKMwIDAQAB";

    public static RSA rsa = new RSA(null, publicKey);

    @Value("${is-prod}")
    private Boolean isProd;

    @Data
    public static class UploadResult {
        private String id;
        private String path;
    }

    public ArchiveServer(
            ArchiveConfig archive,
            PatientArchiveDao patientArchiveDao, SocketV2 socketV2,
            UserCache userCache) {
        this.socketV2 = socketV2;
        this.patientArchiveDao = patientArchiveDao;
        this.archiveData = archive;
        this.userCache = userCache;
    }

    public List<PatientArchive> getPatientArchives(String patNo, Integer times) {
        List<PatientArchive> array = patientArchiveDao.getPatientList(patNo, times);

        return TreeUtilV2.create(array)
                .sort((a) -> {
                    if (a.getSort() == null)
                        return Integer.MAX_VALUE;
                    return a.getSort();
                })
                .execute();
    }

    public ForestRequest<?> sendApi(String url) {
        return sendApi(url, null);
    }

    public ForestRequest<?> sendApi(String url, String code) {
        String now = FastDateFormat.getInstance("yyyy-MM-ddHH:mm").format(new DateTime());
        String currentCode = code == null ? TokenUtil.getInstance().getTokenUserId() : code;
        String string = currentCode + "_" + now;
        String authorization = rsa.encryptBase64(string, KeyType.PublicKey);

        return Forest.post(archiveData.getArchiveUrl() + url)
                .addHeader("content-type", "application/json")
                .addHeader("Authorization", authorization);
    }

    public ForestRequest<?> sendEmrApi(String url) {
        return sendApi("/emr" + url);
    }

    public String submitTask(List<TaskPatient> data) {
        ForestRequest<?> api = sendEmrApi("/submitTask");
        String code = TokenUtil.getInstance().getTokenUserId();
        String name = userCache.getUserInfoByToken().getName();
        for (TaskPatient item : data) {
            item.setSubmitCode(code);
            item.setSubmitName(name);
        }
        api.addBody(data);
        api.execute();
        return "";
    }

    public Object createMergedPDF(String patNo, Integer times) {
        return sendEmrApi("/createMergedPDF")
                .addHeader("content-type", "multipart/form-data")
                .contentFormUrlEncoded()
                .addBody("patNo", patNo)
                .addBody("times", times)
                .execute();
    }

    public ResultVo<String> sort(TaskPatient data) {
        ListUtil.batchList(data.getArchives(), PatientArchiveDao.class, BaseMapper::updateById);
        JSONObject js = new JSONObject() {{
            put("code", "change");
            put("data", new JSONObject() {{
                put("code", TokenUtil.getInstance().getTokenUserId());
                put("message", "排序");
            }});
        }};

        socketV2.sendArchiveMessage(
                ArchiveSocketParam.of(
                        data.getPatNo() + "_" + data.getTimes(),
                        js.toJSONString())
        );

        return ResultVoUtil.success(ExceptionEnum.SUCCESS_AND_EL_MESSAGE, "排序成功。");
    }

    public Object upload(MultipartFile file, String patNo, Integer times, String parent) {
        ForestRequest<?> forestRequest = sendEmrApi("/upload");
        try {
            forestRequest.contentFormUrlEncoded()
                    .addHeader("content-type", "multipart/form-data")
                    .addFile("file", file.getInputStream(), file.getOriginalFilename(), file.getContentType())
                    .addBody("patNo", patNo)
                    .addBody("times", times)
                    .addBody("parent", parent);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return forestRequest.execute();
    }

    public Object delFile(PatientArchive archive) {
        return sendEmrApi("/delFile").addBody(archive).execute();
    }

    public Object addDir(PatientArchive archive) {
        return sendEmrApi("/addDir").addBody(archive).execute();
    }

    public Object rename(String id, String name) {
        return sendEmrApi("/rename")
                .addHeader("content-type", "multipart/form-data")
                .contentFormUrlEncoded()
                .addBody("id", id)
                .addBody("name", name)
                .execute();
    }

    public Object getAllLog(String patNo, Integer times) {
        return sendEmrApi("/getAllLog")
                .addHeader("content-type", "multipart/form-data")
                .contentFormUrlEncoded()
                .addBody("patNo", patNo)
                .addBody("times", times)
                .execute();
    }

    public Object getTaskNameList() {
        ForestRequest<?> forestRequest = sendEmrApi("/getTaskNameList");
        return forestRequest.execute();
    }


    /**
     * 上传文件到服务器，如果需要删除请调用这个接口
     * 自动识别换当前环境
     * {@link #delUploadById(String) }
     *
     * @param file 上传的文件
     * @param path 文件的路径不能包含文件名称 如 xxx.png 是不行的
     * @return 返回文件的id，文件的地址
     */
    public UploadResult uploadFile(MultipartFile file, String path) {
        return uploadFile(file, path, !isProd, null);
    }

    public UploadResult uploadFileByCode(MultipartFile file, String path, String code) {
        return uploadFile(file, path, !isProd, code);
    }


    public UploadResult uploadFile(MultipartFile file, String path, boolean isTest) {
        return uploadFile(file, path, isTest, null);
    }

    /**
     * 上传文件到服务器，如果需要删除请调用这个接口 {@link #delUploadById(String) }
     *
     * @param file   上传的文件
     * @param path   文件的路径不能包含文件名称 如 xxx.png 是不行的
     * @param isTest 是否放入测试的文件夹
     * @return 返回文件的id，文件的地址
     */
    public UploadResult uploadFile(MultipartFile file, String path, boolean isTest, String userCode) {
        JSONObject execute;
        try {
            execute = sendApi("/uploadFile", userCode)
                    .contentFormUrlEncoded()
                    .addHeader("content-type", "multipart/form-data")
                    .addFile("file", file.getInputStream(), file.getOriginalFilename(), file.getContentType())
                    .addBody("path", path)
                    .addBody("isTest", isTest)
                    .execute(JSONObject.class);
        } catch (Exception e) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "上传文件错误:" + e.getMessage());
        }

        Integer code = execute.getInteger("code");

        if (!Objects.equals(ExceptionEnum.SUCCESS.getCode(), code)) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "上传文件错误:" + execute.getString("message"));
        }
        return execute.getObject("data", UploadResult.class);
    }

    public void delUploadById(String id) {
        JSONObject execute = sendApi("/delUploadById/" + id).execute(JSONObject.class);
        if (!Objects.equals(ExceptionEnum.SUCCESS.getCode(), execute.getInteger("code"))) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, execute.getString("message"));
        }
    }

    public UploadResult uploadFileByStr(String data, String path, boolean isTest, String userCode) {
        JSONObject execute;

        Map<String, Object> body = new HashMap<>();
        body.put("data", data);
        body.put("path", path);
        body.put("isTest", isTest);

        try {
            execute = sendApi("/uploadByStr", userCode)
                    .addHeader("content-type", "application/json")
                    .addBody(body)
                    .execute(JSONObject.class);
        } catch (Exception e) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "上传文件错误:" + e.getMessage());
        }
        Integer code = execute.getInteger("code");

        if (!Objects.equals(ExceptionEnum.SUCCESS.getCode(), code)) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "上传文件错误:" + execute.getString("message"));
        }
        return execute.getObject("data", UploadResult.class);
    }


    public UploadResult uploadFileByStr(String data, String path, boolean isTest) {
        return uploadFileByStr(data, path, isTest, null);
    }

    public UploadResult uploadFileByStr(String data, String path) {
        return uploadFileByStr(data, path, !isProd, null);
    }

    public UploadResult uploadFileByBytes(byte[] data, String path, boolean isTest, String userCode) {
        JSONObject execute;

        Map<String, Object> body = new HashMap<>();
        body.put("data", data);
        body.put("path", path);
        body.put("isTest", isTest);

        try {
            execute = sendApi("/uploadByByte", userCode)
                    .addHeader("content-type", "application/json")
                    .addBody(body)
                    .execute(JSONObject.class);
        } catch (Exception e) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "上传文件错误:" + e.getMessage());
        }
        Integer code = execute.getInteger("code");

        if (!Objects.equals(ExceptionEnum.SUCCESS.getCode(), code)) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "上传文件错误:" + execute.getString("message"));
        }
        return execute.getObject("data", UploadResult.class);
    }

    public UploadResult uploadFileByBytes(byte[] data, String path, boolean isTest) {
        return uploadFileByBytes(data, path, isTest, null);
    }

    public UploadResult uploadFileByBytes(byte[] data, String path) {
        return uploadFileByBytes(data, path, !isProd, null);
    }


    /**
     * 创建文件
     *
     * @param file 文件
     * @param path 地址用 /开头
     * @return 返回你传入的地址，如果你是工作集成平台的话可以指定用这个地址访问到
     */
    public String createFile(MultipartFile file, String path) {
        JSONObject execute;
        try {
            execute = sendApi("/createFile").contentFormUrlEncoded()
                    .addHeader("content-type", "multipart/form-data")
                    .addFile("file", file.getInputStream(), file.getOriginalFilename(), file.getContentType())
                    .addBody("path", path)
                    .execute(JSONObject.class);
        } catch (Exception e) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "上传文件错误:" + e.getMessage());
        }
        Integer code = execute.getInteger("code");

        if (!Objects.equals(ExceptionEnum.SUCCESS.getCode(), code)) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, "上传文件错误:" + execute.getString("message"));
        }

        return execute.getObject("data", String.class);
    }


    public String deleteFile(String path) {
        Map<String, String> map = new HashMap<>();
        map.put("path", path);
        JSONObject execute = sendApi("/deleteFile")
                .addBody(map)
                .execute(JSONObject.class);

        Integer code = execute.getInteger("code");
        if (!Objects.equals(ExceptionEnum.SUCCESS.getCode(), code)) {
            throw new BizException(ExceptionEnum.LOGICAL_ERROR, execute.getString("message"));
        }
        return execute.getObject("data", String.class);
    }

}

