package com.friday.custody.service.basicService.impl;

import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.friday.custody.dao.TableInfoDao;
import com.friday.custody.dto.QueryBySQLDto;
import com.friday.custody.dto.TableNameDto;
import com.friday.custody.dto.TableStructureDto;
import com.friday.custody.exception.RRException;
import com.friday.custody.req.ConditionReq;
import com.friday.custody.req.GetDataByTableNameAndSqlReq;
import com.friday.custody.req.TableInfoReq;
import com.friday.custody.service.basicService.TableInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service("tableInfoService")
@Slf4j
@Transactional
public class TableInfoServiceImpl  implements TableInfoService {
    @Autowired
    DataSource dataSource;

    /**
     * 获取所有备注带：标准表 的表
     * @return
     */
    @Override
    public List<TableNameDto> listTable() {
        List<TableNameDto> tables = new ArrayList<>();
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            ResultSet resultSet = metaData.getTables(connection.getCatalog(), "%", null, new String[]{"TABLE"});
            while (resultSet.next()) {
                String tableName = resultSet.getString("TABLE_NAME");
                String remarks = resultSet.getString("REMARKS");
                TableNameDto tableNameDto = TableNameDto.builder().tableName(tableName).tableComment(remarks).build();
                tables.add(tableNameDto);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            log.error(e.toString());
        } finally {
            if (null != connection) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    log.info(e.toString());
                }
            }
        }
        tables = tables.stream().filter(element -> element.getTableComment().contains("标准表")).collect(Collectors.toList());
        return tables;
    }

    /**
     * 根据表名获取表结构，数据类型转成Java格式
     * @param tableName
     * @return
     */
    @Override
    public List<TableStructureDto> listTableColumn(String tableName){
        Configuration config = getConfig();
        List<TableStructureDto> dtos = new ArrayList<>();
        List<Map<String, String>> maps = tableInfoDao.queryColumns(tableName);
        for (Map<String, String> map : maps) {
            TableStructureDto dto = TableStructureDto.builder()
                    .columnName(map.get("columnName"))
                    .columnType(config.getString(map.get("dataType"), columnToJava(map.get("dataType"))))
                    .columnComment(map.get("columnComment"))
                    .build();
            dtos.add(dto);
        }
        dtos = dtos.stream().filter(s ->
                !("version".equals(s.getColumnName())
                        || "unique_key".equals(s.getColumnName())
                        || "remark".equals(s.getColumnName())
                        || "create_time".equals(s.getColumnName())
                        || "update_time".equals(s.getColumnName())
                        || "is_deleted".equals(s.getColumnName()))
        ).collect(Collectors.toList());
        return dtos;
    }
    @Resource
    TableInfoDao tableInfoDao;
    //记录参与计算的list长度
    private int returnValueLength=0;

    /**
     * 根据查询字段，表名，查询条件获取数据
     *
     * @param req
     * @return
     */
    @Override
    public List<QueryBySQLDto> getDataByTableNameAndSql(GetDataByTableNameAndSqlReq req){
        String expression = req.getExpression();
        List<TableInfoReq> requestsList = req.getRequestsList();
        ArrayList<QueryBySQLDto> queryBySQLDtos = new ArrayList<>();
        Map<String, List<?>> dataByReq = new HashMap<>();
        for (int i = 0; i < requestsList.size(); i++) {
            List returnValueList = getDataByReq(requestsList.get(i));
            if (returnValueList.size()>0){
                //用abc代替是防止出现(a-a)/b的情况
                QueryBySQLDto build = QueryBySQLDto.builder().dataList(returnValueList).operationFieldName(requestsList.get(i).getOperationFieldName()).build();
                queryBySQLDtos.add(build);
                dataByReq.put(requestsList.get(i).getOperationFieldName(),returnValueList);
            }else {
                return new ArrayList<>();
            }
        }
        if (StringUtils.isEmpty(expression)){
            return  queryBySQLDtos;
        }
        List<?> returnValueList = getReturnValueList(dataByReq, expression);
        QueryBySQLDto build = QueryBySQLDto.builder().dataList(returnValueList).operationFieldName(expression).build();
        queryBySQLDtos.add(build);
        return  queryBySQLDtos;
    }

    private List<String> methodList = new ArrayList(Arrays.asList("=","<","<=",">",">="));
    /**
     *
     * @param req 请求参数类
     * @return List
     */
    @Override
    public List getDataByReq(TableInfoReq req) {
        //条件字段list
        List<String> reqFieldName=new ArrayList<>();
        //运算符list
        List<String> reqComparisonOperators=new ArrayList<>();
        List<ConditionReq> conditionList = req.getConditionList();
        List<String> fieldNameList = getFieldName(req.getTableName());
        for (int i = 0; i < conditionList.size(); i++) {
            ConditionReq conditionReq = conditionList.get(i);
            reqFieldName.add(conditionReq.getConditionFieldName());
            reqComparisonOperators.add(conditionReq.getComparisonOperators());
        }
        //判断传递的字段名是否存在这张表里面，防止非法字段
        boolean doesItExistFieldName=doesItExist(fieldNameList,reqFieldName);
        boolean doesItExistMethod=doesItExist(methodList,reqComparisonOperators);
        if (doesItExistFieldName && doesItExistMethod){
            try {
                List<Object> dataByTableNameAndSql = tableInfoDao.getDataByTableName(req, conditionList);
                returnValueLength = dataByTableNameAndSql.size();
                return dataByTableNameAndSql;
            }catch (Exception e){
                System.out.println(e);
            }

        }else {
            log.error("字段名或请求方法不规范，字段名={},请求方法={}",reqFieldName,reqComparisonOperators);
            return new ArrayList<>();
        }
        return new ArrayList<>();
    }

    /**
     *
     * @param fullDataList 全量数据list
     * @param beJudged   需判断的list
     * @return boolean
     */
    private boolean doesItExist(List fullDataList,List beJudged){
//判断传递的字段名是否存在这张表里面，防止非法字段
        boolean doesItExist=false;
        try {
            doesItExist = fullDataList.containsAll(beJudged);
        }catch (Exception e){
            log.error(e.toString());
            e.printStackTrace();
        }
        return doesItExist;
    }

    /**
     *根据表达式运算
     */
    List<?> getReturnValueList(Map<String, List<?>> dataByReq,String expression){
        Expression compiledExp = AviatorEvaluator.compile(expression);
        HashMap<String, Object> hashMap = new HashMap<>();
        List<String> returnValueList = new ArrayList<>();
        for (int i = 0; i < returnValueLength; i++) {
            for (String s : dataByReq.keySet()) {
                List<?> dataByReqList = dataByReq.get(s);
                if (dataByReq.get(s).size()>0){
                    Object value = dataByReqList.get(i);
                    //如果为0不能作为分母，待定
                    if (value.equals(0.00)){
                        continue;
                    }
                    hashMap.put(s,value);
                }else {
                    return new ArrayList<>();
                }
            }
            Object execute = compiledExp.execute(hashMap);
            //计算结果使用科学计数法保留四位小数
            String format = new DecimalFormat("#.####E00").format(execute);
            returnValueList.add(format);
        }
        return returnValueList;
    }
    /**
     * 获取该表字段名
     * */
    @Override
    public List<String> getFieldName(String tableName){
        List<TableStructureDto> dtos = listTableColumn(tableName);
        ArrayList<String> list = new ArrayList<>();
        for (TableStructureDto dto : dtos) {
            String columnName = dto.getColumnName();
            list.add(columnName);
        }
        return list;
    }
    /**
     * 获取配置信息
     */
    public static Configuration getConfig() {
        try {
            return new PropertiesConfiguration("generator.properties");
        } catch (Exception e) {
            throw new RRException("获取配置文件失败，", e);
        }
    }
    /**
     * 列名转换成Java属性名
     */
    public static String columnToJava(String columnName) {
        return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
    }
}
