/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.processor.jpa;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.examples.VersionedItem;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;

@Disabled("Need the fix of OPENJPA-2461")
public class JpaRouteSkipLockedEntityTest extends AbstractJpaTest {
    protected static final String SELECT_ALL_STRING = "select x from " + VersionedItem.class.getName() + " x";

    private int count;
    private final ReentrantLock lock = new ReentrantLock();
    private Condition cond1 = lock.newCondition();

    @Test
    public void testRouteJpa() throws Exception {
        MockEndpoint mock1 = getMockEndpoint("mock:result1");
        mock1.expectedMessageCount(2);
        MockEndpoint mock2 = getMockEndpoint("mock:result2");
        mock2.expectedMessageCount(2);

        template.sendBody("jpa://" + VersionedItem.class.getName(), new VersionedItem("one"));
        template.sendBody("jpa://" + VersionedItem.class.getName(), new VersionedItem("two"));
        template.sendBody("jpa://" + VersionedItem.class.getName(), new VersionedItem("three"));
        template.sendBody("jpa://" + VersionedItem.class.getName(), new VersionedItem("four"));

        this.context.getRouteController().startRoute("second");
        this.context.getRouteController().startRoute("first");

        assertMockEndpointsSatisfied();

        //force test to wait till finished
        this.context.getRouteController().stopRoute("first");
        this.context.getRouteController().stopRoute("second");

        setLockTimeout(60);
        List<?> list = entityManager.createQuery(selectAllString()).getResultList();
        assertEquals(0, list.size());
    }

    @Override
    protected RouteBuilder createRouteBuilder() {
        return new RouteBuilder() {
            @Override
            public void configure() {
                String options = "?skipLockedEntity=true"; //&lockModeType=PESSIMISTIC_FORCE_INCREMENT";
                from("jpa://" + VersionedItem.class.getName() + options).routeId("first").autoStartup(false)
                        .bean(new WaitLatch()).log("route1: ${body}").to("mock:result1");
                from("jpa2://select" + options + "&query=select s from VersionedItem s").routeId("second").autoStartup(false)
                        .bean(new WaitLatch()).log("route2: ${body}").to("mock:result2");
            }
        };
    }

    @Override
    protected String routeXml() {
        return "org/apache/camel/processor/jpa/springJpaRouteSkipLockedTest.xml";
    }

    @Override
    protected String selectAllString() {
        return SELECT_ALL_STRING;
    }

    public class WaitLatch {
        public void onMessage(VersionedItem body) throws Exception {
            lock.lock();
            try {

                count++;
                // if (count != 1) {
                cond1.signal();
                // }

                // if not last
                if (count != 4) {
                    cond1.await();
                }
            } finally {
                lock.unlock();
            }
        }
    }

    @Override
    @BeforeEach
    public void setUp() throws Exception {
        super.setUp();
        setLockTimeout(0);
    }

    public void setLockTimeout(int timeout) throws SQLException {
        entityManager.getTransaction().begin();
        Connection connection = entityManager.unwrap(java.sql.Connection.class);
        connection.createStatement()
                .execute("CALL SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.locks.waitTimeout', '" + timeout + "')");
        entityManager.getTransaction().commit();
    }

}
