/*
 * 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.openjpa.persistence.jdbc.annotations;

import jakarta.persistence.EntityManager;

import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.strats.NoneVersionStrategy;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;

/**
 * Test for opt-lock
 *
 * @author Steve Kim
 */
public class TestVersion extends SingleEMFTestCase {

    @Override
    public void setUp() {
        setUp(AnnoTest1.class, AnnoTest2.class, AnnoTest3.class, Flat1.class,
            EmbedOwner.class, EmbedValue.class, CLEAR_TABLES
//            ,"openjpa.Log","SQL=trace"
            ,"openjpa.ConnectionFactoryProperties","printParameters=true"
        );

        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        AnnoTest1 test1 = new AnnoTest1();
        test1.setPk(5L);
        test1.setBasic(50);
        test1.setTransient(500);
        em.persist(test1);

        AnnoTest2 test2 = new AnnoTest2();
        test2.setPk1(5);
        test2.setPk2("bar");
        test2.setBasic("50");
        em.persist(test2);

        AnnoTest3 test3 = new AnnoTest3();
        test3.setPk(3L);
        test3.setBasic2(50);
        em.persist(test3);
        em.getTransaction().commit();
        em.close();
    }

    public void testVersionNumeric() {
        EntityManager em1 = emf.createEntityManager();
        em1.getTransaction().begin();
        EntityManager em2 = emf.createEntityManager();
        em2.getTransaction().begin();

        AnnoTest1 pc1 = em1.find(AnnoTest1.class, 5L);
        AnnoTest1 pc2 = em2.find(AnnoTest1.class, 5L);
        assertEquals(1, pc1.getVersion());
        assertEquals(1, pc2.getVersion());
        assertEquals(0, pc1.getTransient());
        pc1.setBasic(75);
        pc2.setBasic(75);
        em1.getTransaction().commit();
        em1.close();

        em1 = emf.createEntityManager();
        pc1 = em1.find(AnnoTest1.class, 5L);
        assertEquals(2, pc1.getVersion());
        em1.close();
        try {
            em2.getTransaction().commit();
            fail("Optimistic fail");
        } catch (Exception e) {
        } finally {
            em2.close();
        }
    }

    public void testVersionTimestamp() {
        // ensure that some time has passed
        // since the records were created
        try {
            Thread.sleep(5000);
        }
        catch (InterruptedException e) {
            // do nothing
        }

        EntityManager em1 = emf.createEntityManager();
        em1.getTransaction().begin();
        EntityManager em2 = emf.createEntityManager();
        em2.getTransaction().begin();

        AnnoTest2 pc1 = em1.find(AnnoTest2.class,
            new AnnoTest2.Oid(5, "bar"));
        AnnoTest2 pc2 = em2.find(AnnoTest2.class,
            new AnnoTest2.Oid(5, "bar"));
        assertNotNull(pc1.getVersion());
        assertEquals(pc1.getVersion(), pc2.getVersion());
        pc1.setBasic("75");
        pc2.setBasic("75");
        em1.getTransaction().commit();
        em1.close();

        em1 = emf.createEntityManager();
        pc1 = em1.find(AnnoTest2.class,
            new AnnoTest2.Oid(5, "bar"));
        java.util.Date pc1Version = pc1.getVersion();
        java.util.Date pc2Version = pc2.getVersion();
        assertTrue(pc1Version.compareTo(pc2Version) > 0);
        em1.close();
        try {
            em2.getTransaction().commit();
            fail("Optimistic fail");
        } catch (Exception e) {
        } finally {
            em2.close();
        }
    }

    public void testVersionSubclass() {
        EntityManager em1 = emf.createEntityManager();
        em1.getTransaction().begin();
        EntityManager em2 = emf.createEntityManager();
        em2.getTransaction().begin();

        AnnoTest3 pc1 = em1.find(AnnoTest3.class, 3L);
        AnnoTest3 pc2 = em2.find(AnnoTest3.class, 3L);
        assertEquals(1, pc1.getVersion());
        assertEquals(1, pc2.getVersion());
        pc1.setBasic2(75);
        pc2.setBasic2(75);
        em1.getTransaction().commit();
        em1.close();

        em1 = emf.createEntityManager();
        pc1 = em1.find(AnnoTest3.class, 3L);
        assertEquals(2, pc1.getVersion());
        em1.close();
        try {
            em2.getTransaction().commit();
            fail("Optimistic fail");
        } catch (Exception e) {
        } finally {
            em2.close();
        }
    }

    public void testVersionNoChange() {
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();

        AnnoTest1 pc = em.find(AnnoTest1.class, 5L);
        assertEquals(1, pc.getVersion());
        assertEquals(0, pc.getTransient());
        pc.setTransient(750);
        em.getTransaction().commit();
        em.close();

        em = emf.createEntityManager();
        pc = em.find(AnnoTest1.class, 5L);
        assertEquals(1, pc.getVersion());
        assertEquals(0, pc.getTransient());
        em.close();
    }

    public void testNoDefaultVersionWithoutFieldOrColumn() {
        ClassMapping cls = ((JDBCConfiguration) emf.getConfiguration()).
            getMappingRepositoryInstance().getMapping(EmbedOwner.class,
            null, true);
        assertEquals(NoneVersionStrategy.getInstance(),
            cls.getVersion().getStrategy());
        assertEquals(0, cls.getVersion().getColumns().length);
    }

    public void testVersionWithField() {
        ClassMapping cls = ((JDBCConfiguration) emf.getConfiguration()).
            getMappingRepositoryInstance().getMapping(AnnoTest1.class,
            null, true);
        assertTrue(NoneVersionStrategy.getInstance() !=
            cls.getVersion().getStrategy());
        assertEquals(1, cls.getVersion().getColumns().length);
    }

    public void testNullInitialVersion() {
        EntityManager em = emf.createEntityManager();
        EntityManager em2 = emf.createEntityManager();
        try {
            AnnoTest1 e = new AnnoTest1(System.currentTimeMillis());
            em.getTransaction().begin();
            em.persist(e);
            em.createQuery("UPDATE AnnoTest1 a SET a.version=null where a.pk=:pk").setParameter("pk", e.getPk())
                .executeUpdate();
            em.getTransaction().commit();
            em.close();
             em = emf.createEntityManager();

            em.getTransaction().begin();
            em2.getTransaction().begin();

            AnnoTest1 e2 = em2.find(AnnoTest1.class, e.getPk());
            e = em.find(AnnoTest1.class, e.getPk());
            e.setBasic(1);
            em.getTransaction().commit();

            e2 = em2.find(AnnoTest1.class, e.getPk());
            em2.refresh(e2);
            System.out.println(e2.getBasic());
        } finally {
            if (em.getTransaction().isActive()) {
                em.getTransaction().rollback();
            }
            em.close();
        }
    }

}
