package org.sfm.jdbc;

import org.junit.Test;
import org.sfm.beans.DbObject;
import org.sfm.jdbc.named.NamedSqlQuery;
import org.sfm.test.jdbc.DbHelper;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import java.util.List;

import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class QueryPreparerTest {

    JdbcMapperFactory jdbcMapperFactory = JdbcMapperFactory
            .newInstance()
            .addColumnProperty("type_ordinal", SqlTypeColumnProperty.of(Types.NUMERIC));
    @Test
    public void testInsertAndSelectWithPreparedStatementMapper() throws SQLException {
        NamedSqlQuery insertQuery = NamedSqlQuery.parse("INSERT INTO test_db_object(id, name, email, creation_time, type_ordinal, type_name) values(?, ?, ?, ?, ?, ?) ");
        NamedSqlQuery selectQuery = NamedSqlQuery.parse("select id, name, email, creation_time, type_ordinal, type_name from TEST_DB_OBJECT where id = ? ");

        QueryPreparer<DbObject> insertQueryPreparer =
                jdbcMapperFactory
                        .from(DbObject.class).to(insertQuery);

        QueryPreparer<DbObject> selectQueryPreparer =
                jdbcMapperFactory.from(DbObject.class).to(selectQuery);


        DbObject dbObject = DbObject.newInstance();

        Connection connection = DbHelper.objectDb();
        try {
            PreparedStatement ps = insertQueryPreparer.prepare(connection).bind(dbObject);
            try {
                ps.execute();
            } finally {
                ps.close();
            }

            ps = selectQueryPreparer.prepare(connection).bind(dbObject);
            try {
                ps.setLong(1, dbObject.getId());

                ResultSet resultSet = ps.executeQuery();

                assertTrue(resultSet.next());
                assertEquals(dbObject, JdbcMapperFactory.newInstance().newMapper(DbObject.class).map(resultSet));
            } finally {
                ps.close();
            }
        } finally {
            connection.close();
        }
    }

    @Test
    public void testSelectWithInList() throws SQLException {
        NamedSqlQuery selectInListQuery = NamedSqlQuery.parse("select * from Table where name in (?) and id = ? ");
        QueryPreparer<QueryParamList> selectInListMapper = jdbcMapperFactory.from(QueryParamList.class).to(selectInListQuery);
        Connection conn = mock(Connection.class);
        PreparedStatement mps = mock(PreparedStatement.class);

        QueryParamList value = new QueryParamList();
        value.name = Arrays.asList("name1", "name2");
        value.id = 3334;

        when(conn.prepareStatement("select * from Table where name in (?, ?) and id = ? ")).thenReturn(mps);

        PreparedStatement ps = selectInListMapper.prepare(conn).bind(value);

        assertSame(mps, ps);
        verify(mps).setString(1, "name1");
        verify(mps).setString(2, "name2");
        verify(mps).setInt(3, 3334);

    }

    @Test
    public void testSelectWithInArray() throws SQLException {
        NamedSqlQuery selectInListQuery = NamedSqlQuery.parse("select * from Table where name = ? and id in (?) ");
        QueryPreparer<QueryParamArray> selectInListMapper = jdbcMapperFactory.from(QueryParamArray.class).to(selectInListQuery);
        Connection conn = mock(Connection.class);
        PreparedStatement mps = mock(PreparedStatement.class);

        QueryParamArray value = new QueryParamArray();
        value.name = "nannme";
        value.id = new int[] { 3334, 3336 };

        when(conn.prepareStatement("select * from Table where name = ? and id in (?, ?) ")).thenReturn(mps);

        PreparedStatement ps = selectInListMapper.prepare(conn).bind(value);

        assertSame(mps, ps);
        verify(mps).setString(1, "nannme");
        verify(mps).setInt(2, 3334);
        verify(mps).setInt(3, 3336);

    }


    public static class QueryParamArray {
        public String name;
        public int[] id;
    }

    public static class QueryParamList {
        public List<String> name;
        public int id;
    }
}
@@ -373,16 +373,192 @@
 sert.*;%0A
+import static org.mockito.Matchers.anyInt;%0Aimport static org.mockito.Matchers.anyLong;%0Aimport static org.mockito.Matchers.anyString;%0Aimport static org.mockito.Mockito.doThrow;%0A
 import s
@@ -1925,18 +1925,55 @@
         
-ps
+final QueryBinder%3CDbObject%3E queryBinder
  = selec
@@ -2002,38 +2002,90 @@
 (connection)
-.bind(dbObject
+;%0A            ps = selectQueryPreparer.prepareStatement(connection
 );%0A         
@@ -2113,38 +2113,39 @@
     
-ps.setLong(1, dbObject.getId()
+queryBinder.bindTo(dbObject, ps
 );%0A%0A
@@ -4312,16 +4312,871 @@
 %0A    %7D%0A%0A
+    @Test%0A    public void testQueryBinderClosePsOnException() throws SQLException %7B%0A        NamedSqlQuery query = NamedSqlQuery.parse(%22select id, name, email, creation_time, type_ordinal, type_name from TEST_DB_OBJECT where id = ? %22);%0A%0A        QueryPreparer%3CDbObject%3E queryPreparer =%0A                jdbcMapperFactory%0A                        .from(DbObject.class).to(query);%0A%0A        Connection conn = mock(Connection.class);%0A        PreparedStatement ps = mock(PreparedStatement.class);%0A        when(conn.prepareStatement(anyString())).thenReturn(ps);%0A%0A        doThrow(new IllegalStateException()).when(ps).setLong(anyInt(), anyLong());%0A%0A        try %7B%0A            queryPreparer.prepare(conn).bind(DbObject.newInstance());%0A            fail();%0A        %7D catch(IllegalStateException e) %7B%0A            // expected%0A        %7D%0A%0A        verify(ps).close();%0A    %7D%0A
 %0A    pub

add tests