package com.gyee.wisdom.dao.taoscz;

import com.gyee.wisdom.common.data.timeseries.*;
import com.gyee.wisdom.common.exception.WisdomException;
import com.gyee.wisdom.common.utils.StringUtil;
import com.gyee.wisdom.dao.timeseries.ILatestDao;
import com.gyee.wisdom.dao.timeseries.TaosczDao;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Component
@TaosczDao
public class TaosLatestDao implements ILatestDao {

    @Value("${taoscz.db_name:power_test}")
    private String dbName;

    @Autowired
    TaosConfig config;

    @Override
    public Map<String, TsData> getTsDataLatest(List<TsPoint> tsPoints) throws Exception {
        Map<String, TsData> result = new HashMap<>();
        Map<TsDataType, List<TsPoint>> pointGroup = tsPoints.stream().collect(Collectors.groupingBy(TsPoint::getTsDataType));
        for (Map.Entry<TsDataType, List<TsPoint>> entry : pointGroup.entrySet()) {
            String[] tagNames = entry.getValue().stream().map(TsPoint::getId).toArray(String[]::new);
            if (entry.getKey() == TsDataType.DOUBLE) {
                result.putAll(getDoubleTsDataSnapshots(tagNames));
            } else if (entry.getKey() == TsDataType.BOOLEAN) {
                result.putAll(getBooleanTsDataSnapshots(tagNames));
            } else if (entry.getKey() == TsDataType.LONG) {
                result.putAll(getLongTsDataSnapshots(tagNames));
            } else {
                throw new WisdomException("Taos不支持数据类型:" + entry.getKey());
            }
        }

        return result;
    }


    @Override
    public int writeDoubleLatest(List<TsPointData> list) throws Exception {
        Statement st = config.getInstance().createStatement();
        StringBuilder sb = new StringBuilder();
        sb.append("INSERT INTO ");
        for (TsPointData obj : list) {
            long time = obj.getTsData().getTs();
            String point = config.getTableName(obj.getTagName());
            double value = obj.getTsData().getDoubleValue().get();
            // sb.append(TaosCovertUtil.coverStationPrefix(point)).append(".");
            sb.append(point).append(" VALUES (");
            sb.append(time).append(",").append(value).append(") ");
        }
        sb.append(";");
        val i = st.executeUpdate(sb.toString());

        return i == list.size() ? list.size() : 0;
    }

    @Override
    public int writeStringLatest(List<TsPointData> list) throws Exception {
        Statement st = config.getInstance().createStatement();
        StringBuilder sb = new StringBuilder();
        sb.append("insert into ");
        for (TsPointData obj : list) {
            long time = obj.getTsData().getTs();
            String point = obj.getTagName();
            String value = obj.getTsData().getStringValue().get();
            // sb.append(TaosCovertUtil.coverStationPrefix(point)).append(".");
            sb.append(point).append(" values (");
            sb.append(time).append(",'").append(value).append("') ");
        }
        boolean flag = st.execute(sb.toString());

        return flag == true ? list.size() : 0;
    }

    @Override
    public int writeBooleanLatest(List<TsPointData> list) throws Exception {
        Statement st = config.getInstance().createStatement();
        StringBuilder sb = new StringBuilder();
        sb.append("insert into ");
        for (TsPointData obj : list) {
            long time = obj.getTsData().getTs();
            String point = obj.getTagName();
            boolean value = obj.getTsData().getBooleanValue().get();
            // sb.append(TaosCovertUtil.coverStationPrefix(point)).append(".");
            sb.append(point).append(" values (");
            sb.append(time).append(",").append(value).append(") ");
        }
        sb.append(";");
        int i = st.executeUpdate(sb.toString());

        return i;
    }

    @Override
    public int writeLongLatest(List<TsPointData> list) throws Exception {
        Statement st = config.getInstance().createStatement();
        StringBuilder sb = new StringBuilder();
        sb.append("insert into ");
        for (TsPointData obj : list) {
            long time = obj.getTsData().getTs();
            String point = obj.getTagName();
            long value = obj.getTsData().getLongValue().get();
            //sb.append(TaosCovertUtil.coverStationPrefix(point)).append(".");
            sb.append(point).append(" values (");
            sb.append(time).append(",").append(value).append(")");
        }
        boolean flag = st.execute(sb.toString());

        return flag == true ? list.size() : 0;
    }

    @Override
    public int writeBlobLatest(List<TsPointData> list) throws Exception {
        return 0;
    }

    @Override
    public int writeCoordinateLatest(List<TsPointData> list) throws Exception {
        return 0;
    }

    /**
     * 由于taos数据库中全部是DOUBLE类型，故不需要区分类型
     *
     * @param dataList
     * @return
     * @throws Exception
     */
    @Override
    public boolean writeLatest(List<TsPointData> dataList) throws Exception {
        int writeCount = 0;

        Map<TsDataType, List<TsPointData>> pointGroup = dataList.stream().collect(Collectors.groupingBy(TsPointData::findDataType));
        for (Map.Entry<TsDataType, List<TsPointData>> entry : pointGroup.entrySet()) {
            List<TsPointData> pointDataList = entry.getValue();
            if (entry.getKey() == TsDataType.DOUBLE) {
                int count = writeDoubleLatest(pointDataList);
                writeCount = writeCount + count;
            } else if (entry.getKey() == TsDataType.BOOLEAN) {
                int count = writeBooleanLatest(pointDataList);
                writeCount = writeCount + count;
            } else if (entry.getKey() == TsDataType.STRING) {
                int count = writeStringLatest(pointDataList);
                writeCount = writeCount + count;
            } else if (entry.getKey() == TsDataType.BLOB) {
                int count = writeBlobLatest(pointDataList);
                writeCount = writeCount + count;
            } else if (entry.getKey() == TsDataType.COORDINATE) {
                int count = writeCoordinateLatest(pointDataList);
                writeCount = writeCount + count;
            } else if (entry.getKey() == TsDataType.LONG) {
                int count = writeLongLatest(pointDataList);
                writeCount = writeCount + count;
            }
        }
        return writeCount > 0 ? true : false;
    }

    public Map<String, TsData> getDoubleTsDataSnapshots(String... tagNames) throws Exception {
        Map<String, TsData> tsDataMap = new HashMap<>();
        Statement st = config.getInstance().createStatement();

        String inString = Arrays.stream(tagNames).map(s -> "'" + s.toLowerCase() + "'").collect(Collectors.joining(","));
        inString = "select last_row(*),tbname from " + config.getDbName() + "." + config.getAIStableName()
                + " where tbname in (" + inString + ") group by tbname;";
        ResultSet rs = st.executeQuery(inString);

        while (rs.next()) {
            tsDataMap.put(rs.getString(3).toUpperCase(), new DoubleTsData(rs.getTimestamp(1).getTime(), (short) 0, rs.getDouble(2)));
        }
        return tsDataMap;
    }

    public Map<String, TsData> getLongTsDataSnapshots(String... tagNames) throws Exception {
        Map<String, TsData> tsDataMap = new HashMap<>();
        Statement st = config.getInstance().createStatement();
        for (String tag : tagNames) {
            String tableName = config.getTableName(tag);
            ResultSet rs = st.executeQuery("select last_row(*) from " + tableName);
            while (rs.next()) {
                tsDataMap.put(tag, new LongTsData(rs.getTimestamp(1).getTime(), (short) 0, rs.getLong(2)));
            }
        }
        return tsDataMap;
    }

    public Map<String, TsData> getBooleanTsDataSnapshots(String... tagNames) throws Exception {
        Map<String, TsData> tsDataMap = new HashMap<>();
        Statement st = config.getInstance().createStatement();

        String inString = Arrays.stream(tagNames).map(s -> "'" + s.toLowerCase() + "'").collect(Collectors.joining(","));
        inString = "select last_row(*),tbname from " + config.getDbName() + "." + config.getDIStableName() +
                " where tbname in (" + inString + ") group by tbname;";
        ResultSet rs = st.executeQuery(inString);

        while (rs.next()) {
            tsDataMap.put(rs.getString(3).toUpperCase(), new BooleanTsData(rs.getTimestamp(1).getTime(), (short) 0, rs.getBoolean(2)));
        }
        return tsDataMap;
    }

}
