package com.wecise.odb.jdbc;

import com.google.gson.Gson;
import com.wecise.odb.Error;
import com.wecise.odb.Message;
import redis.clients.jedis.Jedis;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class ODBStatement implements Statement {

    private Connection conn;
    private boolean closed;
    private ResultSet rs;
    private ArrayList<String> batch;

    ODBStatement(Connection conn) {
        this.conn = conn;
        this.closed = false;
        this.batch = new ArrayList<>();
    }

    protected void exec(String sql, Object[] values) throws SQLException {
        Jedis ctr = ((ODBConnection)this.conn).getConnector();
//        System.out.println(ctr.getSocket().getRemoteSocketAddress());
        try {
//            HashMap<String, Object> body = new HashMap<>();
//            body.put("keyspace", ((ODBConnection)this.conn).getKeyspace());
//            body.put("stmt", sql);
//            if (values != null && values.length > 0) {
//                body.put("values", values);
//            }

            String valsJson = "[]";
            if (values != null && values.length > 0) {
                valsJson = new Gson().toJson(values);
            }

            Object o = ctr.sendCommand(ODBCommandProtocol.MQL.mql, sql, "meta", valsJson);
            String recvmsg = new String((byte[]) o);

            Gson gs = new Gson();
            Map recvBody = gs.fromJson(recvmsg, Map.class); // {message:{data:[...],meta:{...}}, status:""}
            String status = recvBody.get("status").toString();
            if (status.equals("success")) {
                Map msg = (Map) recvBody.get("message");
                ArrayList data = (ArrayList) msg.get("data");
                Map odbMeta = (Map) msg.get("meta");
                this.rs = new ODBResultSet(data, odbMeta, this);
            } else {
                throw new SQLDataException(recvBody.get("message").toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new SQLDataException(e.toString());
        } finally {
            ((ODBConnection) this.conn).releaseConnector(ctr);
        }
    }

    @Override
    public ResultSet executeQuery(String s) throws SQLException {
        exec(s, null);
        return rs;
    }

    @Override
    public int executeUpdate(String s) throws SQLException {
        executeQuery(s);
        return 0;
    }

    @Override
    public void close() throws SQLException {
        closed = true;
    }

    @Override
    public int getMaxFieldSize() throws SQLException {
        return 0;
    }

    @Override
    public void setMaxFieldSize(int i) throws SQLException {

    }

    @Override
    public int getMaxRows() throws SQLException {
        return 0;
    }

    @Override
    public void setMaxRows(int i) throws SQLException {

    }

    @Override
    public void setEscapeProcessing(boolean b) throws SQLException {

    }

    @Override
    public int getQueryTimeout() throws SQLException {
        return 0;
    }

    @Override
    public void setQueryTimeout(int i) throws SQLException {

    }

    @Override
    public void cancel() throws SQLException {

    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return null;
    }

    @Override
    public void clearWarnings() throws SQLException {

    }

    @Override
    public void setCursorName(String s) throws SQLException {

    }

    @Override
    public boolean execute(String s) throws SQLException {
        executeQuery(s);
        return this.rs != null;
    }

    @Override
    public ResultSet getResultSet() throws SQLException {
        return this.rs;
    }

    @Override
    public int getUpdateCount() throws SQLException {
        return 0;
    }

    @Override
    public boolean getMoreResults() throws SQLException {
        return false;
    }

    @Override
    public void setFetchDirection(int i) throws SQLException {

    }

    @Override
    public int getFetchDirection() throws SQLException {
        return 0;
    }

    @Override
    public void setFetchSize(int i) throws SQLException {

    }

    @Override
    public int getFetchSize() throws SQLException {
        return 0;
    }

    @Override
    public int getResultSetConcurrency() throws SQLException {
        return 0;
    }

    @Override
    public int getResultSetType() throws SQLException {
        return 0;
    }

    @Override
    public void addBatch(String s) throws SQLException {
        this.batch.add(s);
    }

    @Override
    public void clearBatch() throws SQLException {
        this.batch.clear();
    }

    @Override
    public int[] executeBatch() throws SQLException {
        String s = String.join(";", this.batch);
        executeQuery(s);
        return new int[0];
    }

    @Override
    public Connection getConnection() throws SQLException {
        return this.conn;
    }

    @Override
    public boolean getMoreResults(int i) throws SQLException {
        return false;
    }

    @Override
    public ResultSet getGeneratedKeys() throws SQLException {
        return null;
    }

    @Override
    public int executeUpdate(String s, int i) throws SQLException {
        executeQuery(s);
        return 0;
    }

    @Override
    public int executeUpdate(String s, int[] ints) throws SQLException {
        executeQuery(s);
        return 0;
    }

    @Override
    public int executeUpdate(String s, String[] strings) throws SQLException {
        executeQuery(s);
        return 0;
    }

    @Override
    public boolean execute(String s, int i) throws SQLException {
        executeQuery(s);
        return this.rs != null;
    }

    @Override
    public boolean execute(String s, int[] ints) throws SQLException {
        executeQuery(s);
        return this.rs != null;
    }

    @Override
    public boolean execute(String s, String[] strings) throws SQLException {
        executeQuery(s);
        return this.rs != null;
    }

    @Override
    public int getResultSetHoldability() throws SQLException {
        return 0;
    }

    @Override
    public boolean isClosed() throws SQLException {
        return closed;
    }

    @Override
    public void setPoolable(boolean b) throws SQLException {

    }

    @Override
    public boolean isPoolable() throws SQLException {
        return false;
    }

    @Override
    public void closeOnCompletion() throws SQLException {

    }

    @Override
    public boolean isCloseOnCompletion() throws SQLException {
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> aClass) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> aClass) throws SQLException {
        return false;
    }
}
