package com.product.data.sync.service;

import com.google.common.base.Joiner;
import com.product.admin.service.OrganizationCacheService;
import com.product.core.cache.DataPoolCacheImpl;
import com.product.core.dao.BaseDao;
import com.product.core.entity.DataTableEntity;
import com.product.core.entity.FieldSetEntity;
import com.product.core.exception.BaseException;
import com.product.core.spring.context.SpringMVCContextHolder;
import com.product.core.transfer.Transactional;
import com.product.data.sync.config.CmnConst;
import com.product.data.sync.service.ide.IViewDataProcessService;
import com.product.data.sync.util.BatchData;
import com.product.util.SystemParamReplace;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 鎶婅鍥句腑鐨勬暟鎹悓姝ュ埌瀹炰綋琛ㄤ腑
 * 澶栭儴鏁版嵁鍚屾瀹氭椂浠诲姟鎵ц瀹屾垚鍚庤皟鐢ㄦ绫荤殑鏂规硶锛屽弬瑙�
 *
 * @author Administrator
 */
@Service
public class ViewDataProcessService implements IViewDataProcessService {
    @Autowired
    public BaseDao baseDao;


    /**
     * 1銆佷粠杩滅▼鏁版嵁鍚屾鍒版湰鍦板簱
     * 2銆佹煡鎵炬暟鎹鐞嗚〃product_sys_database_sync_processing_config涓叧鑱旇〃瀛楁鍚湁鍚屾琛ㄧ殑涓旂埗uuid瀛楁绛夌┖鐨勫鐞嗛厤缃俊鎭�
     * 锛屾姝ヤ负鎶婄浉鍏宠〃杩涜鍏宠仈鏌ヨ锛屽鐞嗛儴闂ㄣ€佸叕鍙搞€佷汉鍛樹俊鎭紝姝ゆ涓哄閲忓鐞�
     * 3銆佸啀鐢ㄧ埗uuid瀛楁绛夌┖杩欐潯璁板綍鍘绘壘涓嬩竴姝ュ鐞嗚锛屼緷娆℃槸鐢熸垚浜哄憳淇℃伅銆侀儴闂ㄤ俊鎭€佸叕鍙镐俊鎭€傛姝ヤ负鍏ㄩ噺澶勭悊
     * product_sys_database_sync_processing_config涓殑鎵€鏈夐厤缃鐞嗘柟寮忛兘鏄粠view sql涓煡璇㈠嚭鏉ユ彃鍏ュ埌瀹炰綋琛ㄤ腑
     * 瑙嗗浘sql鏌ヨ鍑烘潵鐨勫瓧娈典笌瀹炰綋琛ㄤ腑鐨勫瓧娈靛悕蹇呴』涓€鑷�
     *
     * @param aimTable
     */
    @Override
    @Transactional
    public void copyViewDataToTable(String aimTable) {
        OrgDataMap orgDataMap = null;
        if (StringUtils.isEmpty(aimTable.trim())) {
            return;
        }
        ExecutorService executorService = null;
        DataTableEntity dt = baseDao.listTable("product_sys_database_sync_processing_config", "concat(',',relevance_table,',') like concat('%,',?,',%') and (parent_uuid is null or parent_uuid ='')", new Object[]{aimTable});
        if (!dt.isEmpty()) {
            for (int i = 0; i < dt.getRows(); i++) {
                FieldSetEntity fs = dt.getFieldSetEntity(i);
                String basic_table = fs.getString("relevance_table");
                if (!StringUtils.isEmpty(basic_table.trim())) {
                    String bts[] = basic_table.split(",");
                    //瀵瑰簲鍒悕
                    Map<String, String> aliash = new HashMap<String, String>();
                    if (!StringUtils.isEmpty(fs.getString("table_alias"))) {
                        String alias[] = fs.getString("table_alias").split(",");
                        for (int k = 0; k < bts.length; k++) {
                            if (alias.length > k) {
                                if (alias[k] == null) {
                                    continue;
                                }
                                aliash.put(bts[k], alias[k]);
                            }
                        }
                    }
                    Integer num = fs.getInteger("execute_count");
                    if (bts.length == num.intValue() && !StringUtils.isEmpty(fs.getString("template_sql"))) {//鐩哥瓑璇存槑涓変釜琛ㄩ兘鏇存柊瀹�
                        DataTableEntity sdt = baseDao.listTable("product_sys_database_sync_processing_config_sub", "main_uuid =? ", new String[]{fs.getUUID()});
                        FieldSetEntity table_filter = new FieldSetEntity();
                        table_filter.setTableName("temp");
                        StringBuilder b = new StringBuilder();
                        for (int j = 0; j < sdt.getRows(); j++) {
                            FieldSetEntity fss = sdt.getFieldSetEntity(j);
                            String pkf = baseDao.getPKField(fss.getString("table_name"));//鑾峰彇琛ㄧ殑pk瀛楁
                            if (pkf != null) {
                                if (!StringUtils.isEmpty(aliash.get(fss.getString("table_name")))) {
                                    pkf = aliash.get(fss.getString("table_name")) + "." + pkf;
                                }
                                if (!StringUtils.isEmpty(fss.getString("update_ids"))) {//鏈変慨鏀逛簡鏁版嵁
                                    if (b.length() > 0) {
                                        b.append(" or ");
                                    }
                                    b.append(pkf).append(" in (").append(fss.getString("update_ids")).append(")");
                                }
                                if (!StringUtils.isEmpty(fss.getString("add_scope_id"))) {//鏈夋柊澧炴暟鎹�
                                    if (b.length() > 0) {
                                        b.append(" or ");
                                    }
                                    String scope[] = fss.getString("add_scope_id").split("~");
                                    b.append(pkf).append(" between ").append(scope[0]).append(" and ").append(scope[1]);
                                }
                            } else {
                                //娌℃湁涓婚敭瀛楁锛屾槸鍚︽姏閿�
                                SpringMVCContextHolder.getSystemLogger().error("銆�" + fss.getString("table_name") + "銆戣〃娌℃湁閰嶇疆PK瀛楁銆�");
                            }
                        }
                        //鐢熸垚涓€涓€荤殑鏉′欢琛ㄨ揪寮忥紝濡傛灉娌℃湁锛屽垯continue;
                        if (b.length() > 0) {
                            table_filter.setValue("sync_data_process_data_filter", b.toString());
                        } else {
//                        	continue;
                            table_filter.setValue("sync_data_process_data_filter", "1=1");
                        }
                        //寮€濮嬫浛鎹iew璇彞涓殑鏉′欢琛ㄨ揪寮�
                        String sql = SystemParamReplace.formParamsReplace(fs.getString("template_sql"), table_filter);
                        if (executorService == null) {
                            executorService = Executors.newFixedThreadPool(2);
                        }
                        if (orgDataMap == null) {
                            orgDataMap = new OrgDataMap();
                        }
                        OrgDataMap finalOrgDataMap = orgDataMap;
                        executorService.execute(() -> {
                            BatchData batchData = new BatchData(sql, fs.getString("basic_table"), 50000, 2000);
                            batchData.getDefaultValueMap().put("organization_type", 4);
                            batchData.getDefaultValueMap().put("dept_code", null);

                            fs.setValue("execute_count", 0);
                            baseDao.update(fs);
                            //寮€濮嬪鐞嗘暟鎹敓鎴愪汉鍛�,浠庡悓姝ヨ〃鍒版姤琛ㄥ熀纭€琛�
                            batchData.batchImprovedCoverage(fs.getBoolean("delete_data"), (obj) -> {
                                Map<String, Object> map = (Map<String, Object>) obj[0];
                                String user_id = null;
                                if (map.get("user_id") != null) {
                                    user_id = String.valueOf(map.get("user_id"));
                                }
                                if (map.get(CmnConst.ORG_LEVEL_CODE) != null && map.containsKey(CmnConst.USER_ID) && map.containsKey("dept_code")) {
                                    String code = (String) map.get(CmnConst.ORG_LEVEL_CODE);
                                    if (finalOrgDataMap.isCompany(code)) {
                                        if (user_id != null) {
                                            String deptCode = finalOrgDataMap.getDeptCodeByUser(user_id);
                                            if (!StringUtils.isEmpty(deptCode)) {
                                                map.put("dept_code", deptCode);
                                            }
                                        }
                                    } else if (finalOrgDataMap.isDept(code)) {
                                        String companyCode = finalOrgDataMap.getCompanyCode(code);
                                        if (StringUtils.isEmpty(companyCode)) {
                                            companyCode = finalOrgDataMap.getCompanyCodeByUser(user_id);
                                        }
                                        if (!StringUtils.isEmpty(companyCode)) {
                                            map.put(CmnConst.ORG_LEVEL_CODE, companyCode);
                                            map.put("dept_code", code);
                                        }
                                    }
                                }
                            });
                            batchData.closeConnection();
                            //澶勭悊鏁版嵁鐢熸垚浜哄憳鏁版嵁锛屼粠鍚屾琛ㄥ埌鎶ヨ〃鍩虹琛紝鍐嶇敓鎴愰儴闂ㄣ€佸叕鍙告暟鎹紝鎶ヨ〃鍩虹琛ㄥ唴瀹瑰鐞�
                            lastProcessData(fs.getUUID());
                            baseDao.delete("product_sys_database_sync_processing_config_sub", "main_uuid=?", new Object[]{fs.getUUID()});
                        });

                    }
                }
            }
            if (executorService != null) {
                executorService.shutdown();
                try {
                    executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                } catch (Exception e) {
                    e.printStackTrace();
                    SpringMVCContextHolder.getSystemLogger().error(e);
                }
            }
            SpringMVCContextHolder.getSystemLogger().error("鍚屾澶勭悊鏁版嵁瀹屾垚锛侊紒锛�");
        }
    }


    public class OrgDataMap {

        private Map<String, FieldSetEntity> companyMap = new HashMap<>();

        private Map<String, FieldSetEntity> deptMap = new HashMap<>();

        private Map<String, String> staffDeptMap = new HashMap<>();

        private Map<String, String> staffCompanyMap = new HashMap<>();

        public OrgDataMap() {
            //鍒濆鍖栫粍缁囨満鏋勮〃鏁版嵁
            DataPoolCacheImpl dataPoolCache = DataPoolCacheImpl.getInstance();
//            DataTableEntity companyDt = dataPoolCache.getCacheData("鎵€鏈夊叕鍙镐俊鎭�");
            DataTableEntity companyDt = OrganizationCacheService.getOrgDataStatic("1");
            readRecord(companyDt, this.companyMap);
//            DataTableEntity deptDt = dataPoolCache.getCacheData("鍏徃-閮ㄩ棬");
            DataTableEntity deptDt = OrganizationCacheService.getOrgDataStatic("2");
            readRecord(deptDt, this.deptMap);
            DataTableEntity dt = baseDao.listTable("SELECT user_id,l.org_level_code dept_code,l1.org_level_code company_code FROM `product_sys_staffs` s join  product_sys_org_levels l on s.dept_uuid=l.uuid \n" +
                    "join product_sys_org_levels l1 on s.org_level_uuid=l1.uuid ", new Object[]{});
            if (!DataTableEntity.isEmpty(dt)) {
                DataTableEntity clones = dt.clones();
                for (int i = 0; i < clones.getRows(); i++) {
                    this.staffDeptMap.put(clones.getString(i, "user_id"), clones.getString(i, "dept_code"));
                    this.staffCompanyMap.put(clones.getString(i, "user_id"), clones.getString(i, "company_code"));
                }
            }
        }

        /**
         * 鏍规嵁userid鎵鹃儴闂╟ode
         *
         * @param userId
         * @return
         */
        public String getDeptCodeByUser(String userId) {
            return this.staffDeptMap.get(userId);
        }

        /**
         * 鏍规嵁userid鎵鹃儴闂╟ode
         *
         * @param userId
         * @return
         */
        public String getCompanyCodeByUser(String userId) {
            return this.staffCompanyMap.get(userId);
        }

        /**
         * 鑾峰彇鍏徃缂栫爜鏍规嵁閮ㄩ棬缂栫爜
         *
         * @param deptCode
         * @return
         */
        public String getCompanyCode(String deptCode) {
            if (isExist(deptCode, this.deptMap)) {
                FieldSetEntity fs = this.deptMap.get(deptCode);
                String parentCode = fs.getString("org_level_code_parent");
                String[] code = parentCode.split("-");
                for (int i = 0; i < code.length; i++) {
                    parentCode = "";
                    for (int j = 0; j < code.length - i; j++) {
                        parentCode = (j > 0 ? parentCode + "-" : parentCode) + code[j];
                    }
                    if (isExist(parentCode, this.companyMap)) {
                        return this.companyMap.get(parentCode).getString(CmnConst.ORG_LEVEL_CODE);
                    }
                }
            }
            return null;
        }


        /**
         * 鍒ゆ柇鏄惁涓洪儴闂�
         *
         * @param code
         * @return
         */
        public boolean isDept(String code) {
            return isExist(code, this.deptMap);
        }

        /**
         * 鍒ゆ柇鏄惁涓哄叕鍙�
         *
         * @param code
         * @return
         */
        public boolean isCompany(String code) {
            return isExist(code, this.companyMap);
        }

        /**
         * 鏄惁瀛樺湪
         *
         * @param code 閿�
         * @param map  鏄惁瀛樺湪姝ap
         * @return
         */
        private boolean isExist(String code, Map<String, FieldSetEntity> map) {
            return code != null ? map.get(code) != null : false;
        }


    }


    private void readRecord(DataTableEntity dt, Map<String, FieldSetEntity> map) {
        if (DataTableEntity.isEmpty(dt) || map == null) return;
        for (int i = 0; i < dt.getRows(); i++) {
            FieldSetEntity fs = dt.getFieldSetEntity(i);
            String code = fs.getString(CmnConst.ORG_LEVEL_CODE);
            map.put(code, fs.clones());
        }
    }

    /**
     * 浜屾澶勭悊鏁版嵁鏍规嵁宸插鐞嗙殑鍩虹琛�
     */

    public void firstProcessData() {
        DataTableEntity dt = baseDao.listTable("product_sys_database_sync_processing_config", " (parent_uuid is null or parent_uuid ='')", new Object[]{});
        OrgDataMap orgDataMap = new OrgDataMap();
        FieldSetEntity table_filter = new FieldSetEntity();
        table_filter.setTableName("temp");
        table_filter.setValue("sync_data_process_data_filter", "1=1");
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < dt.getRows(); i++) {
            int finalI = i;
            executorService.execute(() -> {
                FieldSetEntity fs = dt.getFieldSetEntity(finalI);
                String sql = SystemParamReplace.formParamsReplace(fs.getString("template_sql"), table_filter);
                BatchData batchData = new BatchData(sql, fs.getString("basic_table"), 100000, 2000);
                batchData.getDefaultValueMap().put("organization_type", 4);
                batchData.getDefaultValueMap().put("dept_code", null);
                OrgDataMap finalOrgDataMap = orgDataMap;
                //寮€濮嬪鐞嗘暟鎹敓鎴愪汉鍛�,浠庡悓姝ヨ〃鍒版姤琛ㄥ熀纭€琛�
                batchData.batchImprovedCoverage(fs.getBoolean("delete_data"), (obj) -> {
                    Map<String, Object> map = (Map<String, Object>) obj[0];
                    String user_id = null;
                    if (map.get("user_id") != null) {
                        user_id = String.valueOf(map.get("user_id"));
                    }
                    if (map.get(CmnConst.ORG_LEVEL_CODE) != null && map.containsKey(CmnConst.USER_ID) && map.containsKey("dept_code")) {
                        String code = (String) map.get(CmnConst.ORG_LEVEL_CODE);
                        if (finalOrgDataMap.isCompany(code)) {
                            if (user_id != null) {
                                String deptCode = finalOrgDataMap.getDeptCodeByUser(user_id);
                                if (StringUtils.isEmpty(deptCode)) {
                                    map.put("dept_code", deptCode);
                                }
                            }
                        } else if (finalOrgDataMap.isDept(code)) {
                            String companyCode = finalOrgDataMap.getCompanyCode(code);
                            if (StringUtils.isEmpty(companyCode)) {
                                companyCode = finalOrgDataMap.getCompanyCodeByUser(user_id);
                            }
                            if (!StringUtils.isEmpty(companyCode)) {
                                map.put(CmnConst.ORG_LEVEL_CODE, companyCode);
                                map.put("dept_code", code);
                            }
                        }
                    }
                });
                batchData.closeConnection();
                //澶勭悊鏁版嵁鐢熸垚浜哄憳鏁版嵁锛屼粠鍚屾琛ㄥ埌鎶ヨ〃鍩虹琛紝鍐嶇敓鎴愰儴闂ㄣ€佸叕鍙告暟鎹紝鎶ヨ〃鍩虹琛ㄥ唴瀹瑰鐞�
                lastProcessData(fs.getUUID());
            });

        }
        executorService.shutdown();
        while (!executorService.isTerminated()) {

        }
        SpringMVCContextHolder.getSystemLogger().info("缁撴潫浠诲姟firstProcessData锛侊紒锛侊紒锛侊紒锛侊紒锛侊紒锛侊紒");
    }

    /**
     * 鏍规嵁 product_sys_database_sync_processing_config.uuid 鎵ц鏁版嵁澶勭悊
     */

    public void firstProcessData(String parent_uuid) {
        FieldSetEntity fs = baseDao.getFieldSetByFilter("product_sys_database_sync_processing_config", "parent_uuid=?", new Object[]{parent_uuid}, false);
        if (fs != null) {
            firstProcessData(fs);
        }
    }

    /**
     * 鏍规嵁 product_sys_database_sync_processing_config琛ㄤ俊鎭� 鎵ц鏁版嵁澶勭悊
     */

    public void firstProcessData(FieldSetEntity fs) {
        if (fs == null) {
            return;
        }
        OrgDataMap orgDataMap = new OrgDataMap();
        BatchData batchData = new BatchData(fs.getString("template_sql"), fs.getString("basic_table"), 100000, 2000);
        batchData.getDefaultValueMap().put("organization_type", 4);
        if (orgDataMap == null) {
            orgDataMap = new OrgDataMap();
        }
        OrgDataMap finalOrgDataMap = orgDataMap;
        batchData.getDefaultValueMap().put("dept_code", null);
        batchData.batchImprovedCoverage(true, (obj) -> {
            Map<String, Object> map = (Map<String, Object>) obj[0];
            String user_id = null;
            if (map.get("user_id") != null) {
                user_id = String.valueOf(map.get("user_id"));
            }

            if (map.get(CmnConst.ORG_LEVEL_CODE) != null && map.containsKey(CmnConst.USER_ID) && map.containsKey("dept_code")) {
                String code = (String) map.get(CmnConst.ORG_LEVEL_CODE);
                if (finalOrgDataMap.isCompany(code)) {
                    if (user_id != null) {
                        String deptCode = finalOrgDataMap.getDeptCodeByUser(user_id);
                        if (StringUtils.isEmpty(deptCode)) {
                            map.put("dept_code", deptCode);
                        }
                    }
                } else if (finalOrgDataMap.isDept(code)) {

                    String companyCode = finalOrgDataMap.getCompanyCode(code);
                    if (StringUtils.isEmpty(companyCode)) {
                        companyCode = finalOrgDataMap.getCompanyCodeByUser(user_id);
                    }
                    if (!StringUtils.isEmpty(companyCode)) {
                        map.put(CmnConst.ORG_LEVEL_CODE, companyCode);
                        map.put("dept_code", code);
                    }
                }
            }
        });
        batchData.closeConnection();
        lastProcessData(fs.getUUID());
        baseDao.update(fs);
        baseDao.delete("product_sys_database_sync_processing_config_sub", "main_uuid=?", new Object[]{fs.getUUID()});
    }

    public void lastProcessData(String parentUuid) {
        DataTableEntity dt = baseDao.listTable("product_sys_database_sync_processing_config", "parent_uuid=?", new Object[]{parentUuid});
        for (int i = 0; i < dt.getRows(); i++) {
            FieldSetEntity ff = dt.getFieldSetEntity(i);
            if (ff != null && ff.getUUID() != null) {
                this.executeDataProcess(ff.getString("template_sql"), ff.getString("basic_table"), ff.getBoolean("delete_data"));
                this.lastProcessData(ff.getUUID());
            }
        }
    }


    private void executeDataProcess(String sql, String basic_table, boolean deleteData) {
        BatchData batchData = new BatchData(sql, basic_table, 50000, 2000);
        batchData.batchImprovedCoverage(deleteData, null);
        batchData.closeConnection();
    }

    /**
     * 鏇存柊鍚屾璁板綍
     *
     * @param tableName 鍚屾鐩爣琛�
     * @param add_ids   鍚屾淇敼鎴栨柊澧炵殑鏁版嵁ids
     * @param updateIds 鏇存柊鏁版嵁鐨刬ds 閫楀彿鍒嗛殧
     */
    @Override
    @Transactional
    public void updateSyncRecord(String tableName, Integer[] add_ids, String updateIds) throws BaseException {
        DataTableEntity dt = baseDao.listTable("product_sys_database_sync_processing_config", "concat(',',relevance_table,',') like concat('%,',?,',%') and (parent_uuid is null or parent_uuid ='')", new Object[]{tableName});
        if (!DataTableEntity.isEmpty(dt)) {
            for (int i = 0; i < dt.getRows(); i++) {
                if (!StringUtils.isEmpty(updateIds) || (add_ids != null && add_ids.length > 0)) {
                    FieldSetEntity fs = new FieldSetEntity();
                    fs.setTableName("product_sys_database_sync_processing_config_sub");
                    fs.setValue(CmnConst.TABLE_NAME, tableName);
                    if (add_ids != null && add_ids.length > 0) {
                        List<String> idScope = summaryRanges(add_ids);
                        fs.setValue("add_scope_id", Joiner.on(",").join(idScope.toArray()));
                    }
                    fs.setValue("update_ids", updateIds);

                    DataTableEntity dataTableEntity = new DataTableEntity();
                    dataTableEntity.addFieldSetEntity(fs);
                    dt.getFieldSetEntity(i).addSubDataTable(dataTableEntity);
                }
                //鎵ц娆℃暟+1
                Integer execute_count = dt.getInt(i, "execute_count");
                if (execute_count == null) {
                    execute_count = 1;
                } else {
                    execute_count++;
                }
                dt.setFieldValue(i, "execute_count", execute_count);
                baseDao.update(dt.getFieldSetEntity(i));
            }
        }
        this.copyViewDataToTable(tableName);
    }

    /**
     * 鑾峰彇鏁扮粍涓暟瀛楃殑鑼冨洿
     *
     * @param nums 鏁扮粍
     * @return
     */
    public static List<String> summaryRanges(Integer[] nums) {
        Arrays.sort(nums);
        List<String> list = new ArrayList<>();
        if (nums.length == 0) {
            return list;
        }
        int begin = Integer.MIN_VALUE;
        int end = Integer.MIN_VALUE;
        for (int i = 0; i < nums.length; i++) {
            if (i == 0) {
                begin = nums[i];
            } else if (nums[i - 1] < nums[i] - 1) {
                list.add(begin + "~" + end);
                begin = nums[i];
            }
            end = nums[i];
            if (i == nums.length - 1) {
                list.add(begin + "~" + end);
            }
        }
        return list;
    }
}