package cn.com.ty.lift.business.framework.conf;

import cn.com.ty.lift.business.framework.aspect.ValidateMethodInterceptor;
import cn.com.ty.lift.common.aliservice.aliyunoss.AliyunOSS;
import cn.com.ty.lift.common.sql.SqlAnalysisInterceptor;
import cn.com.ty.lift.common.sql.SqlIllegalInterceptor;
import cn.com.ty.lift.common.verify.Validation;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
import com.baomidou.mybatisplus.extension.plugins.SqlExplainInterceptor;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.apache.ibatis.mapping.MappedStatement;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 *     配置bean注入
 * </p>
 * @author wcz
 * @since  2019/12/3
 */
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "aliyun.oss")
public class SystemConfiguration {

    private String endpoint;

    private String bucketName;

    private String accessKeySecret;

    private String accessKeyId;

    @PostConstruct
    private void print(){
        log.info("Hint: the Configuration for Aliyun OSS Client.");
        log.info("============================================================");
        log.info("###| endpoint       : {}", endpoint);
        log.info("###| accessKeyId    : {}", accessKeyId);
        log.info("###| accessKeySecret: {}", accessKeySecret);
        log.info("###| bucketName     : {}", bucketName);
        log.info("============================================================");
    }
    /**
     * 创建aliyun OSS bean,每次使用完连接释放
     */
    public AliyunOSS build(){
        if(StrUtil.hasEmpty(endpoint,accessKeyId,accessKeySecret,bucketName)){
            return AliyunOSS.me();
        }
        return AliyunOSS.me(endpoint,accessKeyId,accessKeySecret,bucketName);
    }

    /**
     * 全局格式化日期
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        return builder -> {
            builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
            builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));
            builder.serializers(new LocalTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));

            builder.deserializers(new LocalDateDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATE_PATTERN)));
            builder.deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
            builder.deserializers(new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN)));

            builder.serializerByType(Long.class, ToStringSerializer.instance);

            builder.serializerByType(BigDecimal.class, BigDecimalSerializer.instance);
            builder.deserializerByType(BigDecimal.class, BigDecimalDeserializer.instance);
        };
    }

    /**
     * mybatis-plus 执行sql性能监测
     */
    @Bean
    @ConditionalOnMissingBean(PerformanceInterceptor.class)
    public SqlAnalysisInterceptor sqlAnalysisInterceptor(){
        SqlAnalysisInterceptor sqlAnalysisInterceptor = new SqlAnalysisInterceptor();
        //格式化执行的sql
        sqlAnalysisInterceptor.setFormat(true);
        //sql写入日志文件
        sqlAnalysisInterceptor.setLogWrite(true);
        return sqlAnalysisInterceptor;
    }

    /**
     * SQL是影响系统性能最重要的因素，所以拦截提示不合理的SQL语句
     */
//    @Bean
    @ConditionalOnMissingBean
    public SqlIllegalInterceptor sqlIllegalInterceptor(){
        SqlIllegalInterceptor sqlIllegalInterceptor = new SqlIllegalInterceptor();
        return sqlIllegalInterceptor;
    }
    /**
     * 防止全表更新与删除
     */
//    @Bean
//    @ConditionalOnMissingBean
    public SqlExplainInterceptor sqlExplainInterceptor(){
        SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
        return sqlExplainInterceptor;
    }

    /**
     * 方法参数校验拦截器,标注了@Verifier的方法，会检查参数，如果参数不满足条件，直接抛出异常
     */
    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor(){
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(new AnnotationMatchingPointcut(RestController.class, Validation.class));
        advisor.setAdvice(new ValidateMethodInterceptor());
        advisor.setOrder(2);
        return advisor;
    }

    /**
     * 多租户 SQL 解析器
     */
//    @Bean
    @ConditionalOnMissingBean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        /*
         * 多租户SQL 解析处理拦截器<br>
         * 动态获取当前登录用户所在的mt_company_id（ 注意观察 SQL ）<br>
         */
        List<ISqlParser> sqlParserList = new ArrayList<>();
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(new TenantHandler() {
            @Override
            public Expression getTenantId() {
                // 该 where 条件 3.2.0 版本开始添加的，用于分区是否为在 where 条件中使用
                // 如果是in/between之类的多个tenantId的情况，参考下方示例
                return new LongValue(1L);
            }

            @Override
            public String getTenantIdColumn() {
                return "mt_company_id";
            }

            @Override
            public boolean doTableFilter(String tableName) {
                // 这里可以判断是否过滤表
            /*
            if ("user".equals(tableName)) {
                return true;
            }*/
                return false;
            }
        });
        sqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(sqlParserList);
        paginationInterceptor.setSqlParserFilter((metaObject) -> {
            MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
            // 过滤自定义查询此时无租户信息约束
            return "com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId());
        });
        return paginationInterceptor;
    }
}
