package com.sdtool.webcore.rest;

import com.sdtool.common.api.*;
import com.sdtool.common.api.mall.MallAdminService;
import com.sdtool.common.api.mall.MallApiCallService;
import com.sdtool.common.api.mall.MallFrontService;
import com.sdtool.common.base.BaseService;
import com.sdtool.common.datas.ERPHeader;
import com.sdtool.common.datas.ERPModule;
import com.sdtool.common.datas.ERPModuleEntity;
import com.sdtool.common.datas.HttpCode;
import com.sdtool.common.entity.base.*;
import com.sdtool.common.entity.doc.BusinessScript;
import com.sdtool.common.entity.site.ERPTokenUser;
import com.sdtool.common.entity.system.KeyValuePair;
import com.dySweetFishPlugin.sql.RMapUtils;
import com.dySweetFishPlugin.tool.lang.ThreadFactoryWithNamePrefix;
import com.sweetfish.convert.json.JsonConvert;
import com.sweetfish.net.http.*;
import com.sweetfish.service.Local;
import com.sweetfish.service.RetResult;
import com.sweetfish.util.AnyValue;
import com.sweetfish.util.AutoLoad;
import org.rex.RMap;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Created by jlutt on 2020-08-14
 *
 * @author jlutt
 */
@AutoLoad(false)
@Local
@RestService(name = "admin", moduleid = 0, comment = "管理模块")
public class AdminRest extends BaseService {

    @Resource
    JsonConvert jsonConvert;

    @Resource
    SystemService systemService;

    @Resource
    DocInfoService docInfoService;

    @Resource
    MallAdminService mallAdminService;

    @Resource
    MallFrontService mallFrontService;

    @Resource
    ConfigService configService;

    @Resource
    LoginService loginService;

    @Resource
    AddressService addressService;

    @Resource
    MallApiCallService mallApiCallService;

    @Resource
    DataSyncService dataSyncService;

    /**
     * 订单线程池
     */
    private ExecutorService execExecutor;

    /**
     * 该方法必须是可以重复调用， 当reload时需要重复调用init方法
     * 远程模式下该方法会重载成空方法
     *
     * @param config 配置参数
     */
    @Override
    public void init(AnyValue config) {
        super.init(config);

        execExecutor = Executors.newFixedThreadPool(4, new ThreadFactoryWithNamePrefix("[管理业务线程池]"));
    }

    /**
     * 进程退出时，调用Service销毁
     * 远程模式下该方法会重载成空方法
     *
     * @param config 配置参数
     */
    @Override
    public void destroy(AnyValue config) {
        super.destroy(config);

        if (execExecutor != null) {
            execExecutor.shutdown();
        }
    }

    @RestMapping(name = "v1/execAdmin", auth = true, logging = 1, perms = "admin:exec:script", sort = 1, comment = "管理", methods = {"POST"})
//    @RestMapping(name = "v1/execAdmin", auth = true, sort = 1, comment = "管理", methods = {"POST"})
    @SuppressWarnings({"rawtypes"})
    public CompletableFuture<RMap> execAdmin(
            @RestParam(name = "businessCode", comment = "脚本名称", required = true) String businessCode,
            @RestParam(name = "execParams", comment = "参数信息", required = true) RMap execParams,
            @RestParam(name = "&", comment = "登录用户，无需传入") ERPTokenUser currentUser,
            @RestHeader(name = ERPHeader.HTTPHEADER_DATASOURCE) String dataSourceId,
            @RestHeader(name = ERPHeader.HTTPHEADER_SUPPLIER) String supplierCode) {
        return CompletableFuture.supplyAsync(
                () -> {

                    RetResult<ProcessMapItem> execResult = handleScript(businessCode, ERPModule.NONE,
                            dataSourceId, Long.parseLong(supplierCode),
                            () -> ProcessMapItem.newBuilder()
                                    .itemData(execParams)
                                    .currentUser(currentUser)
                                    .dataSourceId(dataSourceId)
                                    .supplierCode(Long.parseLong(supplierCode))
                                    .build());
                    if (execResult == null) {
                        return RMapUtils.error(HttpCode.INTERNAL_SERVER_ERROR.value(), "业务脚本不存在");
                    }
                    if (execResult.isSuccess()) {
                        return RMapUtils.successV2(execResult.getResult().getItemData(), null, null);
                    } else {
                        return RMapUtils.error(execResult.getRetcode(), execResult.getRetinfo());
                    }

                }, getExecutor()
        );
    }

    @RestMapping(name = "v1/execAdminAsync", auth = true, logging = 1, perms = "admin:exec:script", sort = 1, comment = "管理", methods = {"POST"})
    @SuppressWarnings({"rawtypes"})
    public CompletableFuture<RMap> execAdminAsync(
            @RestParam(name = "businessCode", comment = "脚本名称", required = true) String businessCode,
            @RestParam(name = "execParams", comment = "参数信息", required = true) RMap execParams,
            @RestParam(name = "&", comment = "登录用户，无需传入") ERPTokenUser currentUser,
            @RestHeader(name = ERPHeader.HTTPHEADER_DATASOURCE) String dataSourceId,
            @RestHeader(name = ERPHeader.HTTPHEADER_SUPPLIER) String supplierCode) {
        return CompletableFuture.supplyAsync(
                () -> {
                    CompletableFuture.runAsync(() -> {
                        RetResult<ProcessMapItem> execResult = handleScript(businessCode, ERPModule.NONE,
                                dataSourceId, Long.parseLong(supplierCode),
                                () -> ProcessMapItem.newBuilder()
                                        .itemData(execParams)
                                        .currentUser(currentUser)
                                        .dataSourceId(dataSourceId)
                                        .supplierCode(Long.parseLong(supplierCode))
                                        .build());
                        if (execResult == null) {
                            logger.error("execAdminAsync:" + businessCode + "业务脚本不存在");
                        } else if (execResult.isSuccess()) {
                            logger.info("execAdminAsync-" + businessCode + "脚本执行结果：" + jsonConvert.convertTo(execResult.getResult().getItemData()));
                        } else {
                            logger.info("execAdminAsync-" + businessCode + "脚本执行错误：" + execResult.getRetinfo());
                        }
                    }, execExecutor);
                    return RMapUtils.successV2("脚本异步执行中，请稍后查看脚本对应的输出信息", null, null);

                }, getExecutor()
        );
    }

    @RestMapping(name = "v1/getLoadedScriptList", auth = true, perms = "admin:query:loadscripts", sort = 2, comment = "获取已加载的业务脚本列表", methods = {"POST"})
    @SuppressWarnings({"rawtypes"})
    @WebApiBean(type = BusinessScript.class, result = true)
    public CompletableFuture<RMap> getLoadedScriptList(
            @RestParam(name = "&", comment = "登录用户，无需传入") ERPTokenUser currentUser,
            @RestHeader(name = ERPHeader.HTTPHEADER_DATASOURCE) String dataSourceId,
            @RestHeader(name = ERPHeader.HTTPHEADER_SUPPLIER) String supplierCode,
            @RestHeader(name = ERPHeader.HTTPHEADER_USERFROM) String userFrom) {
        return CompletableFuture.supplyAsync(
                () -> {

                    List<BusinessScript> scriptList = new ArrayList<>();

                    //此处代码应该根据项目来载入service，不然有些service会是null报错，具体可根据userFrom来判断

                    Stream.of(
                            getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode)),
                            systemService.getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode)),
                            docInfoService.getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode)),
                            mallAdminService == null ? null : mallAdminService.getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode)),
                            mallFrontService == null ? null : mallFrontService.getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode)),
                            configService.getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode)),
                            loginService.getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode)),
                            addressService == null ? null : addressService.getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode)),
                            mallApiCallService == null ? null : mallApiCallService.getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode)),
                            dataSyncService == null ? null : dataSyncService.getLoadedScriptCache(dataSourceId, Long.parseLong(supplierCode))

                    ).filter(Objects::nonNull).forEach(scriptList::addAll);

                    //只保留一个Init_ExpandoGroovy,这里如果正常写需要拆分两个部分，去重用TreeSet操作，只保留一个Init_ExpandoGroovy，写的有些麻烦，懒得处理了，直接用暴力的写法
                    scriptList = scriptList.stream().filter((script) -> !(("Init_ExpandoGroovy".equalsIgnoreCase(script.getBusinessCode())) && (!"_DynLocalSystemServiceImpl".equalsIgnoreCase(script.getLoadServiceClassName())))).collect(Collectors.toList());

                    return RMapUtils.successV2(scriptList, null, null);

                }, getExecutor()
        );
    }

    @RestMapping(name = "v1/invalidateScriptCache", /*auth = true, perms = "admin:query:loadscripts",*/ sort = 3, comment = "重载业务脚本", methods = {"POST"})
    @SuppressWarnings({"rawtypes"})
    public CompletableFuture<RMap> invalidateScriptCache(
            @RestParam(name = "businessCode", comment = "脚本code，对应BusinessScript.businessCode", required = true) String businessCode,
            @RestParam(name = "businessModule", comment = "脚本模块，对应BusinessScript.businessModule", required = true) int businessModule,
            @RestParam(name = "reload", comment = "重新加载", required = true) boolean reload,
            @RestParam(name = "&", comment = "登录用户，无需传入") ERPTokenUser currentUser,
            @RestHeader(name = ERPHeader.HTTPHEADER_DATASOURCE) String dataSourceId,
            @RestHeader(name = ERPHeader.HTTPHEADER_SUPPLIER) String supplierCode) {
        return CompletableFuture.supplyAsync(
                () -> {
                    BusinessOperation operation = (reload) ? BusinessOperation.SUBMIT : BusinessOperation.SAVE;

                    if (businessModule == ERPModule.NONE.getValue()) {
                        if (operation == BusinessOperation.SAVE) {
                            invalidateScriptCache(supplierCode + "-" + businessCode);
                        } else {
                            reloadScriptCache(businessCode, ERPModule.NONE, dataSourceId, Long.parseLong(supplierCode));
                        }
                    }
                    handleScript("DocClearScriptCache", ERPModule.NONE,
                            dataSourceId, Long.parseLong(supplierCode),
                            () -> ProcessEntityItem.<ERPModuleEntity>newBuilder()
                                    .inputItem(new ERPModuleEntity(businessCode, ERPModule.getModule(businessModule)))
                                    .operation(operation)
                                    .currentUser(currentUser)
                                    .dataSourceId(dataSourceId)
                                    .supplierCode(Long.parseLong(supplierCode))
                                    .build());
                    return RMapUtils.success();

                }, getExecutor()
        );
    }


    @RestMapping(name = "v1/updateEsData", auth = true, logging = 1, perms = "admin:exec:script", sort = 4, comment = "调试修改es", methods = {"POST"})
    @SuppressWarnings({"rawtypes"})
    public CompletableFuture<RMap> updateEsData(
            @RestParam(name = "esIndex", comment = "index", required = true) String esIndex,
            @RestParam(name = "esType", comment = "type", required = true) String esType,
            @RestParam(name = "recordId", comment = "对象id", required = true) String recordId,
            @RestParam(name = "valuePairs", comment = "字段与值", required = true) List<KeyValuePair> valuePairs,
            @RestParam(name = "&", comment = "登录用户，无需传入") ERPTokenUser currentUser,
            @RestHeader(name = ERPHeader.HTTPHEADER_DATASOURCE) String dataSourceId,
            @RestHeader(name = ERPHeader.HTTPHEADER_SUPPLIER) String supplierCode) {
        return CompletableFuture.supplyAsync(
                () -> {
                    RetResult result = systemService.updateEsData(esIndex, esType, recordId, valuePairs);
                    if (result.isSuccess()) return RMapUtils.success();
                    else return RMapUtils.error(HttpCode.BAD_REQUEST.value(), result.getRetinfo());
                }, getExecutor()
        );
    }

    @RestMapping(name = "callDataSync", auth = true, sort = 0, logging = 2, comment = "数据同步到ES", methods = {"GET", "POST"})
    public CompletableFuture<InvokeCallResult> callDataSync(
            @RestParam(name = "&", comment = "登录用户，无需传入") ERPTokenUser currentUser,
            @RestBody(comment = "提交数据") InvokeCallParams callParams,
            @RestHeader(name = ERPHeader.HTTPHEADER_DATASOURCE) String dataSourceId,
            @RestHeader(name = ERPHeader.HTTPHEADER_SUPPLIER) String supplierCode) {
        return CompletableFuture.supplyAsync(
                () -> {
                    RetResult<InvokeCallResult> infoResult = dataSyncService.callSync(callParams, currentUser, dataSourceId, Long.parseLong(supplierCode));
                    if (infoResult.isSuccess()) {
                        return infoResult.getResult();
                    }
                    return InvokeCallResult.error(HttpCode.BAD_REQUEST.value(), infoResult.getRetinfo());
                }, getExecutor()
        );
    }
}
