package cn.ibizlab.core.extensions.cql;

import cn.ibizlab.core.extensions.domain.ResultsMQMsg;
import com.datastax.driver.core.*;
import com.datastax.driver.core.schemabuilder.SchemaBuilder;
import com.datastax.driver.mapping.Mapper;
import com.datastax.driver.mapping.MappingManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Repository;

import java.sql.Timestamp;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

import static com.datastax.driver.core.DataType.*;
import static com.datastax.driver.core.querybuilder.QueryBuilder.*;

@Slf4j
@Repository
@ConditionalOnExpression("!''.equals('${cassandra.host:}')")
public class ExecResultRepository {

    private Mapper<ExecResult> mapper;
    private Mapper<ExecResult2> mapper2;
    private Session session;

    private static final String TABLE = "ru_execresult";
    private static final String TABLE2 = "ru_execresult2";

    @Value("${ibiz.batchSize:100}")
    private int batchSize;

    @Value("${ibiz.filterResultsData:false}")
    private boolean filterResultsData;


    public ExecResultRepository(MappingManager mappingManager) {
        createTable(mappingManager.getSession());
        this.mapper = mappingManager.mapper(ExecResult.class);
        this.mapper2 = mappingManager.mapper(ExecResult2.class);
        this.session = mappingManager.getSession();
    }

    private void createTable(Session session) {
        session.execute(
                SchemaBuilder.createTable(TABLE)
                        .ifNotExists()
                        .addPartitionKey("ruleid", text())
                        .addPartitionKey("retvalue", cint())
                        .addClusteringColumn("dimfield", text())
                        .addClusteringColumn("timefield", date())
                        .addClusteringColumn("domainsfield", text())
                        .addClusteringColumn("keyvaluefield", text())
                        .addColumn("ruexecresultname", text())
                        .addColumn("updatedate", date())
                        .addColumn("rulename", text())
                        .addColumn("metricfield", cdouble())
                        .addColumn("ext_1field", text())
                        .addColumn("ext_2field", text())
                        .addColumn("businesscat", text())
                        .withOptions().clusteringOrder("dimfield", SchemaBuilder.Direction.ASC).clusteringOrder("timefield", SchemaBuilder.Direction.DESC).clusteringOrder("domainsfield", SchemaBuilder.Direction.ASC).clusteringOrder("keyvaluefield", SchemaBuilder.Direction.ASC)
        );

        session.execute(
                SchemaBuilder.createTable(TABLE2)
                        .ifNotExists()
                        .addPartitionKey("ruleid", text())
                        .addPartitionKey("keyvaluefield", text())
                        .addClusteringColumn("dimfield", text())
                        .addClusteringColumn("domainsfield", text())
                        .addClusteringColumn("timefield", date())
                        .withOptions().clusteringOrder("dimfield", SchemaBuilder.Direction.ASC).clusteringOrder("domainsfield", SchemaBuilder.Direction.ASC).clusteringOrder("timefield", SchemaBuilder.Direction.DESC)
        );
    }

    public ExecResult find(String country, String firstName, String secondName, UUID id) {
        return mapper.get(country, firstName, secondName, id);
    }

    public List<ExecResult> findAll() {
        final ResultSet result = session.execute(select().all().from(TABLE));
        return mapper.map(result).all();
    }

    public ExecResult save(ExecResult execResult) {
        ExecResult2 execResult2=new ExecResult2();
        execResult2.setRuleid(execResult.getRuleid());
        execResult2.setKeyvaluefield(execResult.getKeyvaluefield());
        execResult2.setDimfield(execResult.getDimfield());
        execResult2.setDomainsfield(execResult.getDomainsfield());
        execResult2.setTimefield(execResult.getTimefield());
        try{
            mapper.save(execResult);
        }catch(Exception ex){
            ex.printStackTrace();
        }
        try{
            mapper2.save(execResult2);
        }catch(Exception ex){
            ex.printStackTrace();
        }
        return execResult;
    }

    public void saveResultsMQMsg(ResultsMQMsg resultsMQMsg) {
        if (filterResultsData) {
            this.saveResultsMQMsg2(resultsMQMsg);
            return;
        }
        final ResultSet result = session.execute(select().all().from(TABLE2).where(in("ruleid",resultsMQMsg.getRules())).and(eq("keyvaluefield",resultsMQMsg.getKeyValueField())));
        List<ExecResult2> list = mapper2.map(result).all();
        for(ExecResult2 execResult2:list) {
            ExecResult execResult=new ExecResult();
            execResult.setRuleid(execResult2.getRuleid());
            execResult.setRetvalue(1);
            execResult.setDimfield(execResult2.getDimfield());
            execResult.setTimefield(execResult2.getTimefield());
            execResult.setDomainsfield(execResult2.getDomainsfield());
            execResult.setKeyvaluefield(execResult2.getKeyvaluefield());
            try{
                mapper.delete(execResult);
            }catch(Exception ex){}
            try{
                mapper2.delete(execResult2);
            }catch(Exception ex){}
        }
        List<ExecResult> results = resultsMQMsg.getDatas();
        if(results != null && results.size() > 0){
            for(ExecResult saveResult : results){
                save(saveResult);
            }
        }
    }

    public void saveResultsMQMsg2(ResultsMQMsg resultsMQMsg){
        log.info("--saveResultsMQMsg2--");
        try {
            final ResultSet result = session.execute(select().all().from(TABLE2).where(in("ruleid", resultsMQMsg.getRules())).and(eq("keyvaluefield", resultsMQMsg.getKeyValueField())));
            List<ExecResult2> list = mapper2.map(result).all();
            List<ExecResult> results = resultsMQMsg.getDatas();
            //过滤需要删除的对象
            if (results != null && results.size() > 0) {
                for (ExecResult result1 : results) {
                    for (ExecResult2 result2 : list) {
                        if (result2.getRuleid().equals(result1.getRuleid()) && result2.getDimfield().equals(result1.getDimfield())
                                && result2.getDomainsfield().equals(result1.getDomainsfield()) && result2.getKeyvaluefield().equals(result1.getKeyvaluefield())
                                && result2.getTimefield().equals(result1.getTimefield())) {
                            list.remove(result2);
                            break;
                        }
                    }
                }
            }
            //删除数据
            PreparedStatement ps = this.getDeleteStatement();
            PreparedStatement ps2 = this.getDeleteStatement2();
            BatchStatement deleteBatch = new BatchStatement();
            deleteBatch.setConsistencyLevel(ConsistencyLevel.ANY);// 设置一致性
            BatchStatement deleteBatch2 = new BatchStatement();
            deleteBatch2.setConsistencyLevel(ConsistencyLevel.ANY);// 设置一致性
            for (ExecResult2 result2 : list) {
                BoundStatement bs = ps.bind(result2.getRuleid(), 1, result2.getDimfield(), result2.getTimefield(), result2.getDomainsfield(), result2.getKeyvaluefield());
                BoundStatement bs2 = ps2.bind(result2.getRuleid(), result2.getKeyvaluefield(), result2.getDimfield(), result2.getDomainsfield(), result2.getTimefield());
                deleteBatch.add(bs);
                deleteBatch2.add(bs2);
                if ( deleteBatch.size() > batchSize) {
                    session.execute(deleteBatch);
                    deleteBatch.clear();
                    session.execute(deleteBatch2);
                    deleteBatch2.clear();
                }
            }
            if (deleteBatch.size() > 0) {
                session.execute(deleteBatch);
                deleteBatch.clear();
                session.execute(deleteBatch2);
                deleteBatch2.clear();
            }
            //保存数据
            for (ExecResult execResult : results) {
                save(execResult);
            }
        } catch (Exception e){
            log.error("cassandra保存错误原因:" + e.getMessage());
        }
    }



    public List<ExecResult> sum(Collection<String> ruleids, Integer retValue, List<String> dims, Timestamp start, Timestamp end) {
        LocalDate st = ExecResult.time2LocalDate(start);
        LocalDate ed = ExecResult.time2LocalDate(end);
        final ResultSet result = session.execute(select().column("ruleid").column("retvalue").column("dimfield").sum("metricfield").as("metricfield").
                from(TABLE).
                where(in("ruleid", ruleids)).
                and(eq("retvalue", retValue)).
                and(in("dimfield", dims)).
                and(gte("timefield", st)).and(lte("timefield", ed)).groupBy("ruleid", "retvalue", "dimfield").limit(5000).setReadTimeoutMillis(200000));
        return mapper.map(result).all();
    }

    /**
     * 增加组织部门的搜索的条件
     * @param ruleids
     * @param retValue
     * @param dims
     * @param start
     * @param end
     * @param ext_1field  组织部门
     * @return
     */
    public List<ExecResult> sum(Collection<String> ruleids, Integer retValue, List<String> dims, Timestamp start, Timestamp end, String ext_1field) {
        LocalDate st = ExecResult.time2LocalDate(start);
        LocalDate ed = ExecResult.time2LocalDate(end);
        final ResultSet result = session.execute(select().column("ruleid").column("retvalue").column("dimfield").sum("metricfield").as("metricfield").
                from(TABLE).
                where(in("ruleid", ruleids)).
                and(eq("retvalue", retValue)).
                and(in("dimfield", dims)).
                and(gte("timefield", st)).and(lte("timefield", ed)).and(eq("ext_1field", ext_1field)).groupBy("ruleid", "retvalue", "dimfield").allowFiltering().limit(5000).setReadTimeoutMillis(200000));
        return mapper.map(result).all();
    }

    public List<ExecResult> avg(Collection<String> ruleids, Integer retValue, List<String> dims, Timestamp start, Timestamp end) {
        LocalDate st = ExecResult.time2LocalDate(start);
        LocalDate ed = ExecResult.time2LocalDate(end);
        final ResultSet result = session.execute(select().column("ruleid").column("retvalue").column("dimfield").avg("metricfield").as("metricfield").
                from(TABLE).
                where(in("ruleid", ruleids)).
                and(eq("retvalue", retValue)).
                and(in("dimfield", dims)).
                and(gte("timefield", st)).and(lte("timefield", ed)).groupBy("ruleid", "retvalue", "dimfield").limit(5000).setReadTimeoutMillis(200000));
        return mapper.map(result).all();
    }

    /**
     * 增加组织部门的搜索的条件
     * @param ruleids
     * @param retValue
     * @param dims
     * @param start
     * @param end
     * @param ext_1field 组织部门
     * @return
     */
    public List<ExecResult> avg(Collection<String> ruleids, Integer retValue, List<String> dims, Timestamp start, Timestamp end, String ext_1field) {
        LocalDate st = ExecResult.time2LocalDate(start);
        LocalDate ed = ExecResult.time2LocalDate(end);
        final ResultSet result = session.execute(select().column("ruleid").column("retvalue").column("dimfield").avg("metricfield").as("metricfield").
                from(TABLE).
                where(in("ruleid", ruleids)).
                and(eq("retvalue", retValue)).
                and(in("dimfield", dims)).
                and(gte("timefield", st)).and(lte("timefield", ed)).and(eq("ext_1field",ext_1field)).groupBy("ruleid", "retvalue", "dimfield").allowFiltering().limit(5000).setReadTimeoutMillis(200000));
        return mapper.map(result).all();
    }

    public List<ExecResult> group(Collection<String> ruleids, Integer retValue, List<String> dims, String type, Timestamp start, Timestamp end) {
        if (type.equalsIgnoreCase("avg"))
            return avg(ruleids, retValue, dims, start, end);
        else
            return sum(ruleids, retValue, dims, start, end);
    }

    public List<ExecResult> group(List<String> ruleids, Integer retValue, List<String> dims, Timestamp start, Timestamp end) {

        return group(ruleids, retValue, dims, "sum", start, end);
    }

    /**
     * 根据规则ID、单位和时间查询相应的规则结果数据
     *
     * @param ruleid
     * @param retValue
     * @param dims
     * @param start
     * @param end
     * @return
     */
    public ResultSet getPageData(String ruleid, PagingState pagingState, Integer retValue, String dims, Timestamp start, Timestamp end) {
        final int RESULTS_PER_PAGE = 1000;
        return getPageData(ruleid, pagingState, RESULTS_PER_PAGE, retValue, dims, start, end);
    }

    /**
     * 根据规则ID、单位和时间查询相应的规则结果数据
     * @param ruleid
     * @param pagingState
     * @param retValue
     * @param dims
     * @param start
     * @param end
     * @param ext_1field
     * @return
     */
    public ResultSet getPageData(String ruleid, PagingState pagingState, Integer retValue, String dims, Timestamp start, Timestamp end, String ext_1field) {
        final int RESULTS_PER_PAGE = 1000;
        return getPageData(ruleid, pagingState, RESULTS_PER_PAGE, retValue, dims, start, end, ext_1field);
    }

    /**
     * 根据规则ID、单位和时间查询相应的规则结果数据
     *
     * @param ruleid
     * @param retValue
     * @param dims
     * @param start
     * @param end
     * @return
     */
    public ResultSet getPageData(String ruleid, PagingState pagingState, int pageSize, Integer retValue, String dims, Timestamp start, Timestamp end) {
        LocalDate st = ExecResult.time2LocalDate(start);
        LocalDate ed = ExecResult.time2LocalDate(end);
        Statement statement = select().column("keyvaluefield").
                from(TABLE).
                where(eq("ruleid", ruleid)).
                and(eq("retvalue", retValue)).
                and(eq("dimfield", dims)).
                and(gte("timefield", st)).and(lte("timefield", ed)).setReadTimeoutMillis(200000);
        statement.setFetchSize(pageSize);
        if (pagingState != null) {
            statement.setPagingState(pagingState);
        }
        final ResultSet result = session.execute(statement);
        return result;
    }

    /**
     * 根据规则ID、单位和时间查询相应的规则结果数据
     * @param ruleid
     * @param pagingState
     * @param pageSize
     * @param retValue
     * @param dims
     * @param start
     * @param end
     * @return
     */
    public ResultSet getPageData(String ruleid, PagingState pagingState, int pageSize, Integer retValue, String dims, Timestamp start, Timestamp end, String ext_1field) {
        LocalDate st = ExecResult.time2LocalDate(start);
        LocalDate ed = ExecResult.time2LocalDate(end);
        Statement statement = select().column("keyvaluefield").
                from(TABLE).
                where(eq("ruleid", ruleid)).
                and(eq("retvalue", retValue)).
                and(eq("dimfield", dims)).
                and(gte("timefield", st)).and(lte("timefield", ed)).and(eq("ext_1field",ext_1field)).allowFiltering().setReadTimeoutMillis(200000);
        statement.setFetchSize(pageSize);
        if (pagingState != null) {
            statement.setPagingState(pagingState);
        }
        final ResultSet result = session.execute(statement);
        return result;
    }

    /**
     * 计算绩效数据的信访件数
     *
     * @param ruleids
     * @param retValue
     * @param dims
     * @param start
     * @param end
     * @return
     */
    public List<ExecResult> sumPerformancePiece(List<String> ruleids, Integer retValue, List<String> dims, Timestamp start, Timestamp end) {
        LocalDate st = ExecResult.time2LocalDate(start);
        LocalDate ed = ExecResult.time2LocalDate(end);
        final ResultSet result = session.execute(select().column("dimfield").column("keyvaluefield").
                from(TABLE).
                where(in("ruleid", ruleids)).
                and(eq("retvalue", retValue)).
                and(in("dimfield", dims)).
                and(gte("timefield", st)).and(lte("timefield", ed)).limit(5000).setReadTimeoutMillis(200000));
        return mapper.map(result).all();
    }

    private PreparedStatement deleteStatement = null;
    public PreparedStatement getDeleteStatement(){
        if(deleteStatement == null){
            deleteStatement = session.prepare("delete from ru_execresult where ruleid = ? and retvalue = ? and dimfield = ? and timefield=? and domainsfield=? and keyvaluefield=?");
        }
        return deleteStatement;
    }

    private PreparedStatement deleteStatement2 = null;
    public PreparedStatement getDeleteStatement2(){
        if(deleteStatement2 == null){
            deleteStatement2 = session.prepare("delete from ru_execresult2 where ruleid = ? and keyvaluefield=?  and dimfield = ? and domainsfield=? and timefield=?  ");
        }
        return deleteStatement2;
    }


    /**
     * 根据指标标识，业务数据主键，删除cql结果数据
     * @param ruleids
     * @param keyvaluefield
     * @return
     */
    public void deleteData(List<String> ruleids, String keyvaluefield){
        final ResultSet result = session.execute(select().all().from(TABLE2).where(in("ruleid",ruleids)).and(eq("keyvaluefield",keyvaluefield)));
        List<ExecResult2> list = mapper2.map(result).all();
        if (list.size() > 0){
            doDelete(list);
        }
    }

    /**
     * 删除数据
     * @param list
     */
    private void doDelete(List<ExecResult2> list){
        for(ExecResult2 execResult2 : list) {
            ExecResult execResult = new ExecResult();
            execResult.setRuleid(execResult2.getRuleid());
            execResult.setRetvalue(1);
            execResult.setDimfield(execResult2.getDimfield());
            execResult.setTimefield(execResult2.getTimefield());
            execResult.setDomainsfield(execResult2.getDomainsfield());
            execResult.setKeyvaluefield(execResult2.getKeyvaluefield());
            try{
                mapper.delete(execResult);
            }catch(Exception ex){}
            try{
                mapper2.delete(execResult2);
            }catch(Exception ex){}
        }
    }

    /**
     * 根据指标标识，删除结果数据
     * @param ruleids
     * @return
     */
    public void deleteData(List<String> ruleids){
        session.execute(delete().all().from(TABLE).where(in("ruleid",ruleids)).and(eq("retvalue",1)));
    }

}
