package com.product.patch.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.product.common.io.FileUtils;
import com.product.common.lang.StringUtils;
import com.product.core.config.Global;
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.service.support.AbstractBaseService;
import com.product.patch.config.CmnConst;
import com.product.patch.config.ErrorCode;
import com.product.patch.service.idel.IPatchExtractService;
import com.product.patch.util.RSAUtil;
import com.product.util.BaseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Copyright 漏 6c
 *
 * @Date: 2022-04-02 16:59
 * @Author: 6c
 * @Description: 琛ヤ竵鎶藉彇
 */
@Service
public class PatchExtractService extends AbstractBaseService implements IPatchExtractService {
    @Autowired
    private BaseDao baseDao;

    public void testExtract() {
        FieldSetEntity fse = new FieldSetEntity();
        fse.setTableName("temp_table");
        fse.setValue(CmnConst.FIELD_CLIENT_UUID, "a34f8818-d36d-4d82-b1b0-dbc0d2e70a03");
        fse.setValue("deploy_date", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
        fse.setValue("operator", "admin");

        JSONObject paramObj = new JSONObject();

        // 琛ㄦ暟鎹�
        JSONObject tableReportObj = new JSONObject();
        tableReportObj.put("6c_test_contract", "id<5");
        tableReportObj.put("6c_test_renewal", "id<3");
        paramObj.put(CmnConst.PARAM_TABLE_REPORT, tableReportObj.toJSONString());

        // 琛ㄧ粨鏋�
        JSONArray tableStructureArray = new JSONArray();
        JSONObject tableStructureObj = new JSONObject();
        tableStructureObj.put("table_name", "6c_test_contract");
        tableStructureObj.put("field", "title,money");
        tableStructureObj.put("index", "id,uuid");
        tableStructureArray.add(tableStructureObj);
        paramObj.put(CmnConst.PARAM_TABLE_REPORT, tableStructureArray.toJSONString());

        // 鍔熻兘寤烘ā
        JSONObject functionObj = new JSONObject();
        functionObj.put("c4abd684-0788-4a39-9422-3c24bc73d2bc", "1,2,3");
        functionObj.put("d0edf393-db19-44b1-87aa-95d28751b0d8", "1,2");
        tableStructureArray.add(functionObj);
        paramObj.put(CmnConst.PARAM_FUNCTION, functionObj.toJSONString());

        // 娴佺▼寤烘ā
        JSONObject flowObj = new JSONObject();
        flowObj.put("7c93182a-d091-4a07-b223-56b9bb9fcde8", "1,2,3");
        tableStructureArray.add(functionObj);
        paramObj.put(CmnConst.PARAM_FLOW, flowObj.toJSONString());

        fse.setValue(CmnConst.DATA, paramObj);
        extract(fse);
    }

    /**
     * 鑾峰彇琛ㄣ€佽鍥俱€佺储寮曚俊鎭�
     * @return
     */
    public JSONObject listTableInfo() {
        String dataBaseName = baseDao.getDataBaseName();
        JSONObject resultObj = new JSONObject();

        StringBuilder sql = new StringBuilder(512);
        sql.append("\nselect table_name");
        sql.append("\nfrom information_schema.`TABLES`");
        sql.append("\nwhere table_schema=?");
        sql.append("\nand table_type='BASE TABLE'");
        sql.append("\norder by table_name");
        DataTableEntity dte = baseDao.listTable(sql.toString(), new Object[]{dataBaseName});
        resultObj.put(CmnConst.PARAM_TABLE_REPORT, BaseUtil.dataTableEntityToJson(dte));

        sql.setLength(0);
        sql.append("\nselect *");
        sql.append("\nfrom (");
        sql.append("\n    select table_name,1 table_type,1 operate_type,column_name name");
        sql.append("\n    from information_schema.`COLUMNS`");
        sql.append("\n    where table_schema=?");
        sql.append("\n    and table_name in (");
        sql.append("\n        select table_name");
        sql.append("\n        from information_schema.`TABLES`");
        sql.append("\n        where table_schema=?");
        sql.append("\n        and table_type='BASE TABLE'");
        sql.append("\n    )");
        sql.append("\n    union all");
        sql.append("\n    select table_name,1 table_type,2 operate_type,column_name name");
        sql.append("\n    from information_schema.`STATISTICS`");
        sql.append("\n    where table_schema=?");
        sql.append("\n    and table_name in (");
        sql.append("\n        select table_name");
        sql.append("\n        from information_schema.`TABLES`");
        sql.append("\n        where table_schema=?");
        sql.append("\n        and table_type='BASE TABLE'");
        sql.append("\n    )");
        sql.append("\n    union all");
        sql.append("\n    select table_name,2 table_type,null,null");
        sql.append("\n    from information_schema.`VIEWS`");
        sql.append("\n    where table_schema=?");
        sql.append("\n    order by table_name");
        sql.append("\n) t");
        sql.append("\norder by table_type,table_name,operate_type,name");
        dte = baseDao.listTable(sql.toString(), new Object[]{dataBaseName, dataBaseName, dataBaseName, dataBaseName, dataBaseName});
        resultObj.put(CmnConst.PARAM_TABLE_STRUCTURE, BaseUtil.dataTableEntityToJson(dte));

        sql.setLength(0);
        sql.append("\nselect uuid,tricode,function_name,t.*");
        sql.append("\nfrom product_sys_functions f,(");
        sql.append("\n    select \"鎸夐挳\" type union all select \"椤甸潰\" union all select \"杩炵嚎\"");
        sql.append("\n) t");
        sql.append("\nwhere data_type=1");
        sql.append("\norder by tricode");
        dte = baseDao.listTable(sql.toString(), new Object[]{});
        resultObj.put(CmnConst.PARAM_FUNCTION, BaseUtil.dataTableEntityToJson(dte));

        sql.setLength(0);
        sql.append("\nselect uuid,title,t.*");
        sql.append("\nfrom product_sys_flow_model f,(");
        sql.append("\n    select \"鑺傜偣\" type union all select \"澶勭悊鍣╘" union all select \"杩炵嚎\"");
        sql.append("\n) t");
        sql.append("\nwhere length(type_code)>0");
        sql.append("\norder by title");
        dte = baseDao.listTable(sql.toString(), new Object[]{});
        resultObj.put(CmnConst.PARAM_FLOW, BaseUtil.dataTableEntityToJson(dte));

        return resultObj;
    }

    /**
     * 琛ヤ竵鎶藉彇
     * @param fse
     */
    public void extract(FieldSetEntity fse) {
        String aimClientUUID = fse.getString(CmnConst.FIELD_CLIENT_UUID);
        if (StringUtils.isEmpty(aimClientUUID)) {
            throw new BaseException(ErrorCode.EXTRACT_NO_AIM_CLIENT_UUID);
        }
        JSONObject paramObj = JSON.parseObject(fse.getString(CmnConst.DATA));
        JSONArray resultArray = new JSONArray();
        // 琛ㄦ暟鎹�
        resultArray.addAll(extractTableRecord(paramObj.getJSONObject(CmnConst.PARAM_TABLE_REPORT)));
        // 琛ㄧ粨鏋�
        resultArray.addAll(extractTableStructure(paramObj.getJSONArray(CmnConst.PARAM_TABLE_STRUCTURE)));
        // 鍔熻兘寤烘ā
        resultArray.addAll(extractFunction(paramObj.getJSONArray(CmnConst.PARAM_FUNCTION)));
        // 娴佺▼寤烘ā
        resultArray.addAll(extractTableStructure(paramObj.getJSONArray(CmnConst.PARAM_FLOW)));

        Date deployDate = fse.getDate(CmnConst.PARAM_DEPLOY_DATE);
        String operator = fse.getString(CmnConst.PARAM_OPERATOR);
        writeToFile(resultArray.toString(), deployDate, operator, aimClientUUID);
    }

    /**
     * 琛ㄦ暟鎹�
     * @param paramObj
     */
    private JSONArray extractTableRecord(JSONObject paramObj) {
        DataTableEntity dte;
        JSONArray resultArray = new JSONArray();
        for (Map.Entry<String, Object> entry : paramObj.entrySet()) {
            dte = baseDao.listTable(entry.getKey(), entry.getValue() == null ? "" : entry.getValue().toString(), new Object[]{});
            resultArray.addAll(dte.toJson());
        }
        return resultArray;
    }

    /**
     * 琛ㄧ粨鏋�
     * @param paramArr
     */
    private JSONArray extractTableStructure(JSONArray paramArr) {
        JSONArray resultArray = new JSONArray();
        JSONObject resultObj;
        JSONObject tempObj;
        for (Object obj : paramArr) {
            tempObj = (JSONObject) obj;
            if ("1".equals(tempObj.getString(CmnConst.TABLE_TYPE))) {
                // 琛�
                if ("1".equals(tempObj.getString(CmnConst.OPERATE_TYPE))) {
                    // 瀛楁
                    resultObj = getTable(tempObj);
                } else if ("2".equals(tempObj.getString(CmnConst.OPERATE_TYPE))) {
                    // 绱㈠紩
                    resultObj = getIndex(tempObj);
                } else {
                    throw new BaseException(ErrorCode.EXTRACT_INVALID_OPERATE_TYPE);
                }
            } else if ("2".equals(tempObj.getString(CmnConst.TABLE_TYPE))) {
                // 瑙嗗浘
                resultObj = getView(tempObj);
            } else {
                throw new BaseException(ErrorCode.EXTRACT_INVALID_TABLE_TYPE);
            }
            if (resultObj != null && !resultObj.isEmpty()) {
                resultArray.add(resultObj);
            }
        }
        return resultArray;
    }

    /**
     * 琛ㄧ粨鏋�-瀛楁
     * @param jsonObject
     * @return
     */
    private JSONObject getTable(JSONObject jsonObject) {
        String name = jsonObject.getString(CmnConst.NAME);
        if (StringUtils.isEmpty(name)) {
            return new JSONObject();
        }
        String[] nameArr = name.split(",");
        String dataBaseName = baseDao.getDataBaseName();
        List<String> paramList = Lists.newArrayList();
        paramList.add(dataBaseName);
        paramList.add(jsonObject.getString(CmnConst.TABLE_NAME));
        paramList.addAll(Arrays.asList(nameArr));
        String filter = String.format("%s=? and %s='%s' and %s=? and %s", CmnConst.DATABASE_COLUMN, CmnConst.TABLE_TYPE_COLUMN, CmnConst.TABLE_TYPE_TABLE, CmnConst.TABLE_NAME, BaseUtil.buildQuestionMarkFilter(CmnConst.COLUMNS_NAME_COLUMN, nameArr.length, true));
        String tableName = String.format("%s.`%s`", CmnConst.DATABASE, CmnConst.TABLE);
        FieldSetEntity fse = baseDao.getFieldSetByFilter(tableName, filter, paramList.toArray(), false);
        return BaseUtil.fieldSetEntityToJson(fse);
    }

    /**
     * 琛ㄧ粨鏋�-绱㈠紩
     * @param jsonObject
     * @return
     */
    private JSONObject getIndex(JSONObject jsonObject) {
        String name = jsonObject.getString(CmnConst.NAME);
        if (StringUtils.isEmpty(name)) {
            return new JSONObject();
        }
        String[] nameArr = name.split(",");
        String dataBaseName = baseDao.getDataBaseName();
        List<String> paramList = Lists.newArrayList();
        paramList.add(dataBaseName);
        paramList.add(jsonObject.getString(CmnConst.TABLE_NAME));
        paramList.addAll(Arrays.asList(nameArr));
        String filter = String.format("%s=? and %s=? and %s=?", CmnConst.DATABASE_COLUMN, CmnConst.TABLE_NAME, BaseUtil.buildQuestionMarkFilter(CmnConst.INDEX_NAME_COLUMN, nameArr.length, true));
        String tableName = String.format("%s.`%s`", CmnConst.DATABASE, CmnConst.INDEX);
        FieldSetEntity fse = baseDao.getFieldSetByFilter(tableName, filter, paramList.toArray(), false);
        return BaseUtil.fieldSetEntityToJson(fse);
    }

    /**
     * 琛ㄧ粨鏋�-瑙嗗浘
     * @param jsonObject
     * @return
     */
    private JSONObject getView(JSONObject jsonObject) {
        String name = jsonObject.getString(CmnConst.NAME);
        if (StringUtils.isEmpty(name)) {
            return new JSONObject();
        }
        String[] nameArr = name.split(",");
        String dataBaseName = baseDao.getDataBaseName();
        List<String> paramList = Lists.newArrayList();
        paramList.add(dataBaseName);
        paramList.add(jsonObject.getString(CmnConst.TABLE_NAME));
        paramList.addAll(Arrays.asList(nameArr));
        String filter = String.format("%s=? and %s=? and %s=?", CmnConst.DATABASE_COLUMN, CmnConst.TABLE_NAME, BaseUtil.buildQuestionMarkFilter(CmnConst.INDEX_NAME_COLUMN, nameArr.length, true));
        String tableName = String.format("%s.`%s`", CmnConst.DATABASE, CmnConst.VIEWS);
        FieldSetEntity fse = baseDao.getFieldSetByFilter(tableName, filter, paramList.toArray(), false);
        return BaseUtil.fieldSetEntityToJson(fse);
    }

    /**
     * 鍔熻兘寤烘ā
     * @param paramArr
     * @return
     */
    private JSONArray extractFunction(JSONArray paramArr) {
        JSONArray resultArray = new JSONArray();
        JSONObject paramObj;
        for (int i = 0;i < paramArr.size();i++) {
            paramObj = paramArr.getJSONObject(i);
            // todo
//            resultArray.add();
        }
        return resultArray;
    }

    /**
     * 琛ヤ竵杈撳嚭鍒版枃浠�
     * @param patchContent
     * @param deployDate
     * @param operator
     * @param clientUUID
     */
    private void writeToFile(String patchContent, Date deployDate, String operator, String clientUUID) {
        deployDate = deployDate == null ? new Date() : deployDate;
        operator = StringUtils.isEmpty(operator) ? "admin" : operator;
        String patchDir = Global.getSystemConfig("patch.extract.dir","");
        File dir = new File(patchDir);
        dir.mkdirs();
        String deployDateStr = new SimpleDateFormat(CmnConst.DATE_FORMAT_1).format(deployDate);
        String fileName = patchDir + File.separator + deployDateStr + "-" + operator + ".patch";
        File file = new File(fileName);
        boolean appendFlag = false;
        if (file.exists()) {
            appendFlag = true;
        } else {
            try {
                file.createNewFile();
            } catch (Exception e) {
                throw new BaseException(ErrorCode.EXTRACT_CREATE_FILE_FAIL);
            }
        }
        StringBuilder content = new StringBuilder(1024);
        content.append(deployDateStr).append("-").append(operator).append(";\n")
                .append(patchContent);
        String encodedData = content.toString();

        // 鍔犲瘑
        FieldSetEntity mainFse = baseDao.getFieldSetEntityByFilter(CmnConst.TABLE_PRODUCT_SYS_ORG_LEVELS, "org_level_id=1", new Object[]{}, false);
        FieldSetEntity mainCryptFse = baseDao.getFieldSetEntityByFilter(CmnConst.TABLE_PRODUCT_SYS_PATCH_KEY, "org_level_uuid=?", new Object[]{mainFse.getUUID()}, false);
        String  mainPrivateKey = mainCryptFse.getString(CmnConst.FIELD_PIRVATE_KEY);
        FieldSetEntity clientCryptFse = baseDao.getFieldSetEntityByFilter(CmnConst.TABLE_PRODUCT_SYS_PATCH_KEY, "org_level_uuid=?", new Object[]{clientUUID}, false);
        String clientPublicKey = clientCryptFse.getString(CmnConst.FIELD_PUBLIC_KEY);
        try {
            encodedData = RSAUtil.privateEncrypt(encodedData, RSAUtil.getPrivateKey(mainPrivateKey));
            encodedData = RSAUtil.publicEncrypt(encodedData, RSAUtil.getPublicKey(clientPublicKey));
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(ErrorCode.EXTRACT_ENCODE_FAIL);
        }

        FileUtils.writeToFile(fileName, encodedData, "utf-8", appendFlag);
    }
}