/*
 * Copyright 2017, TeamDev Ltd. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.server.storage.jdbc.query;

import com.google.protobuf.Any;
import com.google.protobuf.Message;
import org.slf4j.Logger;
import io.spine.server.storage.jdbc.GivenDataSource;
import io.spine.server.storage.jdbc.util.ConnectionWrapper;
import io.spine.server.storage.jdbc.util.DataSourceWrapper;
import io.spine.server.storage.jdbc.util.IdColumn;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class Given {

    private static Logger loggerMock = null;
    private static final IdColumn<String> idColumnMock = mock(IdColumn.StringIdColumn.class);

    // TODO:2016-08-02:alexander.litus: these methods are used in one place only (each), so move them there.
    // Apply this for all such cases in all Given classes.

    static SelectByIdQueryMock getSelectByIdQueryMock() throws SQLException {
        loggerMock = mock(Logger.class);
        final DataSourceWrapper dataSourceMock =
                GivenDataSource.whichThrowsExceptionOnExecuteStatement();
        final SelectByIdQueryMock.Builder builder =
                SelectByIdQueryMock.newBuilder()
                                   .setDataSource(dataSourceMock)
                                   .setLogger(loggerMock)
                                   .setIdColumn(idColumnMock);
        return builder.build();
    }

    static SelectByIdQueryMock getSelectByIdQueryReturningEmptyResultSetMock() throws SQLException {
        loggerMock = mock(Logger.class);

        final DataSourceWrapper dataSourceMock = mock(DataSourceWrapper.class);
        final ConnectionWrapper connectionMock = mock(ConnectionWrapper.class);
        final PreparedStatement preparedStatementMock = mock(PreparedStatement.class);
        final IdColumn<String> idColumnMock = mock(IdColumn.StringIdColumn.class);
        final ResultSet resultSetMock = mock(ResultSet.class);

        when(dataSourceMock.getConnection(anyBoolean())).thenReturn(connectionMock);
        when(connectionMock.prepareStatement(anyString())).thenReturn(preparedStatementMock);
        when(preparedStatementMock.executeQuery()).thenReturn(resultSetMock);
        when(resultSetMock.next()).thenReturn(true);
        when(resultSetMock.getBytes(anyString())).thenReturn(null);

        final SelectByIdQueryMock.Builder builder =
                SelectByIdQueryMock.newBuilder()
                                   .setDataSource(dataSourceMock)
                                   .setLogger(loggerMock)
                                   .setIdColumn(idColumnMock)
                                   .setMessageColumnName(anyString())
                                   .setMessageDescriptor(Any.getDescriptor());
        return builder.build();
    }

    static WriteQueryMock getWriteQueryMock() throws SQLException {
        loggerMock = mock(Logger.class);
        final DataSourceWrapper dataSourceMock =
                GivenDataSource.whichThrowsExceptionOnExecuteStatement();
        final WriteQueryMock.Builder builder = WriteQueryMock.newBuilder()
                                                             .setDataSource(dataSourceMock)
                                                             .setLogger(loggerMock);
        return builder.build();
    }

    static WriteRecordQueryMock getWriteRecordQueryMock() throws SQLException {
        loggerMock = mock(Logger.class);
        final DataSourceWrapper dataSourceMock =
                GivenDataSource.whichThrowsExceptionOnExecuteStatement();
        final WriteRecordQueryMock.Builder builder =
                WriteRecordQueryMock.newBuilder()
                                    .setDataSource(dataSourceMock)
                                    .setLogger(loggerMock)
                                    .setIdColumn(idColumnMock);
//                                    .setRecord(recordMock);
        return builder.build();
    }

    private static final Any recordMock = Any.getDefaultInstance();

    private static class SelectByIdQueryMock extends SelectMessageByIdQuery<String, Message> {

        protected SelectByIdQueryMock(Builder builder) {
            super(builder);
        }

        public static Builder newBuilder() {
            final Builder builder = new Builder();
            builder.setQuery("");
            return builder;
        }

        @SuppressWarnings("ClassNameSameAsAncestorName")
        public static class Builder extends SelectMessageByIdQuery.Builder<Builder,
                                                                    SelectByIdQueryMock,
                                                                    String,
                                                                    Message> {

            @Override
            public SelectByIdQueryMock build() {
                return new SelectByIdQueryMock(this);
            }

            @Override
            protected Builder getThis() {
                return this;
            }
        }
    }

    private static class WriteRecordQueryMock extends WriteRecordQuery<String, Message> {

        protected WriteRecordQueryMock(Builder builder) {
            super(builder);
        }

        public static Builder newBuilder() {
            final Builder builder = new Builder();
            builder.setQuery("");
            return builder;
        }

        @SuppressWarnings("ClassNameSameAsAncestorName")
        public static class Builder extends WriteRecordQuery.Builder<Builder,
                                                                     WriteRecordQueryMock,
                                                                     String,
                                                                     Message> {

            @Override
            public WriteRecordQueryMock build() {
                return new WriteRecordQueryMock(this);
            }

            @Override
            protected Builder getThis() {
                return this;
            }
        }
    }

    private static class WriteQueryMock extends WriteQuery {

        protected WriteQueryMock(Builder builder) {
            super(builder);
        }

        public static Builder newBuilder() {
            final Builder builder = new Builder();
            builder.setQuery("");
            return builder;
        }

        @SuppressWarnings("ClassNameSameAsAncestorName")
        public static class Builder extends WriteQuery.Builder<Builder, WriteQueryMock> {

            @Override
            public WriteQueryMock build() {
                return new WriteQueryMock(this);
            }

            @Override
            protected Builder getThis() {
                return this;
            }
        }
    }

    private Given() {
    }

    static Logger getLoggerMock() {
        return loggerMock;
    }
}

@@ -1123,16 +1123,123 @@
 essage;%0A
+import io.spine.server.entity.EntityRecord;%0Aimport io.spine.server.entity.storage.EntityRecordWithColumns;%0A
 import o
@@ -5091,12 +5091,9 @@
 ock)
-;%0A//
+%0A
     
@@ -5216,28 +5216,96 @@
 nal 
-Any recordMock = Any
+EntityRecordWithColumns recordMock =%0A            EntityRecordWithColumns.of(EntityRecord
 .get
@@ -5321,16 +5321,17 @@
 stance()
+)
 ;%0A%0A    p

Fix write query tests.