package com.taosdata.jdbc;

import com.taosdata.jdbc.enums.SchemalessProtocolType;
import com.taosdata.jdbc.enums.SchemalessTimestampType;
import org.junit.Test;

import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class TSDBJNIConnectorTest {

    private static TSDBResultSetRowData rowData;

    @Test
    public void test() throws SQLException {
        try {
            //change sleepSeconds when debugging with attach to process to find PID
            int sleepSeconds = -1;
            if (sleepSeconds > 0) {
                RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
                String jvmName = runtimeBean.getName();
                long pid = Long.valueOf(jvmName.split("@")[0]);
                System.out.println("JVM PID  = " + pid);

                Thread.sleep(sleepSeconds * 1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // init
        Properties properties = new Properties();
        properties.setProperty(TSDBDriver.PROPERTY_KEY_CONFIG_DIR, "/etc/taos");
        TSDBJNIConnector.init(properties);

        // connect
        TSDBJNIConnector connector = new TSDBJNIConnector();
        connector.connect("127.0.0.1", 6030, null, "root", "taosdata");

        // setup
        String setupSqlStrs[] = {"create database if not exists d precision \"us\"",
                "create table if not exists d.t(ts timestamp, f int)",
                "create database if not exists d2",
                "create table if not exists d2.t2(ts timestamp, f int)",
                "insert into d.t values(now+100s, 100)",
                "insert into d2.t2 values(now+200s, 200)"
        };
        for (String setupSqlStr : setupSqlStrs) {
            long setupSql = connector.executeQuery(setupSqlStr);

            assertEquals(0, connector.getResultTimePrecision(setupSql));
            if (connector.isUpdateQuery(setupSql)) {
                connector.freeResultSet(setupSql);
            }
        }

        {
            long sqlObj1 = connector.executeQuery("select * from d2.t2");
            assertEquals(0, connector.getResultTimePrecision(sqlObj1));
            List<ColumnMetaData> columnMetaDataList = new ArrayList<>();
            int code = connector.getSchemaMetaData(sqlObj1, columnMetaDataList);
            rowData = new TSDBResultSetRowData(columnMetaDataList.size());
            assertTrue(next(connector, sqlObj1));
            assertEquals(0, connector.getResultTimePrecision(sqlObj1));
            connector.freeResultSet(sqlObj1);
        }

        // executeQuery
        long pSql = connector.executeQuery("select * from d.t");

        if (connector.isUpdateQuery(pSql)) {
            connector.freeResultSet(pSql);
            throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_INVALID_WITH_EXECUTEQUERY);
        }

        assertEquals(1, connector.getResultTimePrecision(pSql));

        // get schema
        List<ColumnMetaData> columnMetaDataList = new ArrayList<>();
        int code = connector.getSchemaMetaData(pSql, columnMetaDataList);
        if (code == TSDBConstants.JNI_CONNECTION_NULL) {
            throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_JNI_CONNECTION_NULL);
        }
        if (code == TSDBConstants.JNI_RESULT_SET_NULL) {
            throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_JNI_RESULT_SET_NULL);
        }
        if (code == TSDBConstants.JNI_NUM_OF_FIELDS_0) {
            throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_JNI_NUM_OF_FIELDS_0);
        }

        assertEquals(1, connector.getResultTimePrecision(pSql));
        int columnSize = columnMetaDataList.size();
        // print metadata
        for (int i = 0; i < columnSize; i++) {
//            System.out.println(columnMetaDataList.get(i));
        }
        rowData = new TSDBResultSetRowData(columnSize);
        // iterate resultSet
        for (int i = 0; next(connector, pSql); i++) {
            assertEquals(1, connector.getResultTimePrecision(pSql));
        }
        // close resultSet
        code = connector.freeResultSet(pSql);
        if (code == TSDBConstants.JNI_CONNECTION_NULL) {
            throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_JNI_CONNECTION_NULL);
        } else if (code == TSDBConstants.JNI_RESULT_SET_NULL) {
            throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_JNI_RESULT_SET_NULL);
        }
        // close statement
        connector.executeQuery("use d");
        String[] lines = new String[]{
                "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000",
                "st,t1=4i64,t3=\"t4\",t2=5f64,t4=5f64 c1=3i64,c3=L\"passitagin\",c2=true,c4=5f64,c5=5f64 1626006833640000000"};
        connector.insertLines(lines, SchemalessProtocolType.LINE, SchemalessTimestampType.NANO_SECONDS);

        // close connection
        connector.executeQuery("drop database if exists d");
        connector.executeQuery("drop database if exists d2");
        connector.closeConnection();
    }

    private static boolean next(TSDBJNIConnector connector, long pSql) throws SQLException {
        if (rowData != null)
            rowData.clear();

        int code = connector.fetchRow(pSql, rowData);
        if (code == TSDBConstants.JNI_CONNECTION_NULL) {
            throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_JNI_CONNECTION_NULL);
        } else if (code == TSDBConstants.JNI_RESULT_SET_NULL) {
            throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_JNI_RESULT_SET_NULL);
        } else if (code == TSDBConstants.JNI_NUM_OF_FIELDS_0) {
            throw TSDBError.createSQLException(TSDBErrorNumbers.ERROR_JNI_NUM_OF_FIELDS_0);
        } else return code != TSDBConstants.JNI_FETCH_END;
    }

}
