package cn.stylefeng.team.project.modular.businessrender.sql;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.stylefeng.roses.kernel.rule.exception.base.ServiceException;
import cn.stylefeng.team.generator.api.CodeGeneratorApi;
import cn.stylefeng.team.meta.db.pojo.query.FilterItem;
import cn.stylefeng.team.meta.db.pojo.query.Group;
import cn.stylefeng.team.meta.db.querybuilder.QueryConditionBuilder;
import cn.stylefeng.team.project.api.SqlProviderApi;
import cn.stylefeng.team.project.api.pojo.SqlProviderResult;
import cn.stylefeng.team.project.modular.business.enums.BusinessExceptionEnum;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.util.*;

import static cn.stylefeng.team.project.api.constants.ProjectConstants.PROJECT_MYBATIS_PARAMS;

@Service
public class SqlProviderImpl implements SqlProviderApi {

    @Resource
    private CodeGeneratorApi codeGeneratorApi;

    public static final String SELECT_SQL_TEMPLATE = "select ${tool.joinSqlFieldCodes(fieldNames)} from `${tableName}`";

    public static final String SELECT_SQL_TEMPLATE_WITH_CONDITION = "select ${tool.joinSqlFieldCodes(fieldNames)} from `${tableName}` where ${condition}";

    public static final String SELECT_BY_ID_TEMPLATE_MYBATIS = "select * from `${" + PROJECT_MYBATIS_PARAMS + ".tableName}` where `id` = #{" + PROJECT_MYBATIS_PARAMS + ".primaryKeyId}";

    public static final String SELECT_BY_FIELD_TEMPLATE_MYBATIS = "select * from `${" + PROJECT_MYBATIS_PARAMS + ".tableName}` where `${" + PROJECT_MYBATIS_PARAMS + ".fieldCode}` = #{" + PROJECT_MYBATIS_PARAMS + ".primaryKeyId}";

    public static final String DELETE_BY_FIELD_TEMPLATE_MYBATIS = "DELETE FROM `${" + PROJECT_MYBATIS_PARAMS + ".tableName}` \n" +
            "  WHERE `${" + PROJECT_MYBATIS_PARAMS + ".fieldCode}` IN \n" +
            "  <foreach item=\"item\" index=\"index\" collection=\"" + PROJECT_MYBATIS_PARAMS + ".fieldValueList\" open=\"(\" separator=\",\" close=\")\">\n" +
            "    #{item}\n" +
            "  </foreach>";

    public static final String INSERT_TEMPLATE_MYBATIS = "INSERT INTO `${" + PROJECT_MYBATIS_PARAMS + ".tableName}` \n" +
            "  <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n" +
            "    <foreach item=\"entry\" index=\"key\" collection=\"" + PROJECT_MYBATIS_PARAMS + ".fieldCodeValueMap\" separator=\",\">\n" +
            "      ${key}\n" +
            "    </foreach>\n" +
            "  </trim>\n" +
            "  VALUES\n" +
            "  <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n" +
            "    <foreach item=\"value\" index=\"key\" collection=\"" + PROJECT_MYBATIS_PARAMS + ".fieldCodeValueMap\" separator=\",\">\n" +
            "      #{value}\n" +
            "    </foreach>\n" +
            "  </trim>";

    public static final String UPDATE_TEMPLATE_MYBATIS = "UPDATE `${" + PROJECT_MYBATIS_PARAMS + ".tableName}`\n" +
            "  <set>\n" +
            "    <foreach item=\"value\" index=\"key\" collection=\"" + PROJECT_MYBATIS_PARAMS + ".data\" separator=\",\">\n" +
            "      ${key} = #{value}\n" +
            "    </foreach>\n" +
            "  </set>\n" +
            "  WHERE `id` = #{" + PROJECT_MYBATIS_PARAMS + ".idValue}";

    public static final String BATCH_INSERT_TEMPLATE_MYBATIS = "INSERT INTO `${" + PROJECT_MYBATIS_PARAMS + ".tableName}` \n" +
            "  (<foreach item=\"item\" collection=\"" + PROJECT_MYBATIS_PARAMS + ".tableColumnList\" separator=\",\">\n" +
            "    ${item}\n" +
            "  </foreach>)\n" +
            "  VALUES\n" +
            "  <foreach collection=\"" + PROJECT_MYBATIS_PARAMS + ".fieldCodeValueMapList\" item=\"row\" separator=\",\">\n" +
            "    (<foreach item=\"value\" collection=\"row\" separator=\",\">\n" +
            "      #{value}\n" +
            "    </foreach>)\n" +
            "  </foreach>";

    @Override
    public String selectSql(List<String> fieldNames, String tableName) {
        if (ObjectUtil.isEmpty(fieldNames) || ObjectUtil.isEmpty(tableName)) {
            throw new ServiceException(BusinessExceptionEnum.SQL_TEMPLATE_EMPTY_PARAM_ERROR);
        }

        HashMap<String, Object> param = new HashMap<>();
        param.put("fieldNames", fieldNames);
        param.put("tableName", tableName);
        return codeGeneratorApi.codeGenerate(SELECT_SQL_TEMPLATE, param);
    }

    @Override
    public String selectSql(List<String> fieldNames, String tableName, Map<String, Object> businessSearchParams) {
        if (ObjectUtil.isEmpty(businessSearchParams)) {
            return selectSql(fieldNames, tableName);
        }

        Iterator<Map.Entry<String, Object>> iterator = businessSearchParams.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> entry = iterator.next();
            if (entry.getValue() == null) {
                iterator.remove();
            } else if (entry.getValue() instanceof String && StrUtil.isBlank((String) entry.getValue())) {
                iterator.remove();
            }
        }
        if (ObjectUtil.isEmpty(businessSearchParams)) {
            return selectSql(fieldNames, tableName);
        }

        if (ObjectUtil.isEmpty(fieldNames) || ObjectUtil.isEmpty(tableName)) {
            throw new ServiceException(BusinessExceptionEnum.SQL_TEMPLATE_EMPTY_PARAM_ERROR);
        }

        List<FilterItem> list = new ArrayList<>();
        for (Map.Entry<String, Object> paramEntry : businessSearchParams.entrySet()) {
            list.add(new FilterItem(paramEntry.getKey(), "=", paramEntry.getValue(), "and", null, null));
        }
        String whereSql = new QueryConditionBuilder().build(new Group(list));

        HashMap<String, Object> param = new HashMap<>();
        param.put("fieldNames", fieldNames);
        param.put("tableName", tableName);
        param.put("condition", whereSql);
        return codeGeneratorApi.codeGenerate(SELECT_SQL_TEMPLATE_WITH_CONDITION, param);
    }

    @Override
    public String selectSqlLimitCreateUser(List<String> fieldNames, String tableName, Long createUserId) {

        String originSql = this.selectSql(fieldNames, tableName);

        StringBuilder sql = new StringBuilder(originSql);
        sql.append("\n").append("where create_user = '").append(createUserId).append("'");
        return sql.toString();
    }

    @Override
    public SqlProviderResult selectById(String tableName, String primaryKeyId) {

        if (ObjectUtil.isEmpty(tableName) || ObjectUtil.isEmpty(primaryKeyId)) {
            throw new ServiceException(BusinessExceptionEnum.SQL_TEMPLATE_EMPTY_PARAM_ERROR);
        }

        HashMap<String, Object> param = new HashMap<>();
        param.put("tableName", tableName);
        param.put("primaryKeyId", primaryKeyId);
        return new SqlProviderResult(SELECT_BY_ID_TEMPLATE_MYBATIS, param);
    }

    @Override
    public SqlProviderResult selectByField(String tableName, String fieldCode, String primaryKeyId) {

        if (ObjectUtil.isEmpty(tableName)) {
            throw new ServiceException(BusinessExceptionEnum.SQL_TEMPLATE_EMPTY_PARAM_ERROR);
        }

        HashMap<String, Object> param = new HashMap<>();
        param.put("tableName", tableName);
        param.put("fieldCode", fieldCode);
        param.put("primaryKeyId", primaryKeyId);
        return new SqlProviderResult(SELECT_BY_FIELD_TEMPLATE_MYBATIS, param);
    }

    @Override
    public SqlProviderResult deleteByField(String tableName, String fieldCode, List<String> fieldValue) {

        if (ObjectUtil.isEmpty(tableName) || ObjectUtil.isEmpty(fieldCode) || ObjectUtil.isEmpty(fieldValue)) {
            throw new ServiceException(BusinessExceptionEnum.SQL_TEMPLATE_EMPTY_PARAM_ERROR);
        }

        HashMap<String, Object> param = new HashMap<>();
        param.put("tableName", tableName);
        param.put("fieldCode", fieldCode);
        param.put("fieldValueList", fieldValue);

        return new SqlProviderResult(DELETE_BY_FIELD_TEMPLATE_MYBATIS, param);
    }

    @Override
    public SqlProviderResult insert(String tableName, Map<String, Object> tableData) {

        if (ObjectUtil.isEmpty(tableName) || ObjectUtil.isEmpty(tableData)) {
            throw new ServiceException(BusinessExceptionEnum.SQL_TEMPLATE_EMPTY_PARAM_ERROR);
        }

        HashMap<String, Object> param = new HashMap<>();
        param.put("tableName", tableName);
        param.put("fieldCodeValueMap", tableData);

        return new SqlProviderResult(INSERT_TEMPLATE_MYBATIS, param);
    }

    @Override
    public SqlProviderResult update(String tableName, Long primaryKeyId, Map<String, Object> data) {

        if (ObjectUtil.isEmpty(tableName) || ObjectUtil.isEmpty(data)) {
            throw new ServiceException(BusinessExceptionEnum.SQL_TEMPLATE_EMPTY_PARAM_ERROR);
        }

        HashMap<String, Object> beetlParams = new HashMap<>();
        beetlParams.put("tableName", tableName);
        beetlParams.put("data", data);
        beetlParams.put("idValue", primaryKeyId);

        return new SqlProviderResult(UPDATE_TEMPLATE_MYBATIS, beetlParams);
    }

    @Override
    public SqlProviderResult batchInsert(String tableName, List<Map<String, Object>> tableDataList) {

        if (ObjectUtil.isEmpty(tableName) || ObjectUtil.isEmpty(tableDataList)) {
            throw new ServiceException(BusinessExceptionEnum.SQL_TEMPLATE_EMPTY_PARAM_ERROR);
        }

        for (Map<String, Object> tableData : tableDataList) {
            if (ObjectUtil.isEmpty(tableData)) {
                throw new ServiceException(BusinessExceptionEnum.SQL_TEMPLATE_EMPTY_PARAM_ERROR);
            }
        }



        List<String> columns = new ArrayList<>();
        for (Map<String, Object> tableData : tableDataList) {
            for (String key : tableData.keySet()) {
                if (!columns.contains(key)) {
                    columns.add(key);
                }
            }
        }

        List<List<Object>> normalizedValuesList = new ArrayList<>();
        for (Map<String, Object> tableData : tableDataList) {
            List<Object> rowValues = new ArrayList<>();
            for (String column : columns) {
                rowValues.add(tableData.getOrDefault(column, null));
            }
            normalizedValuesList.add(rowValues);
        }

        Map<String, Object> generateParam = new HashMap<>();
        generateParam.put("tableName", tableName);
        generateParam.put("tableColumnList", columns);
        generateParam.put("fieldCodeValueMapList", normalizedValuesList);

        return new SqlProviderResult(BATCH_INSERT_TEMPLATE_MYBATIS, generateParam);
    }

}
