/*
 * 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.jdbc.kernel;

import java.util.Collection;
import java.util.Iterator;

import jakarta.persistence.EntityManager;

import org.apache.openjpa.persistence.test.SingleEMFTestCase;

public class TestM2MBiVersion extends SingleEMFTestCase {
    public static String SALESID = "SALES";
    public static String MARKETINGID = "MARKETING";

    public static String EMPLOYEE1ID = "EMPLOYEE1";
    public static String EMPLOYEE2ID = "EMPLOYEE2";
    public static String EMPLOYEE3ID = "EMPLOYEE3";

    @Override
    public void setUp() {
        setUp(
                M2MBiDepartment.class,
                M2MBiEmployee.class,
                CLEAR_TABLES);

        createEntities();
    }

    void createEntities() {
        EntityManager em = emf.createEntityManager();

        em.getTransaction().begin();
        M2MBiDepartment sales = new M2MBiDepartment();
        sales.setDeptid(SALESID);
        sales.setName("SALES");
        sales.setCostCode("1000");
        M2MBiDepartment marketing = new M2MBiDepartment();
        marketing.setDeptid(MARKETINGID);
        marketing.setName("marketing");
        marketing.setCostCode("3000");

        M2MBiEmployee e1 = new M2MBiEmployee();
        M2MBiEmployee e2 = new M2MBiEmployee();
        e1.setEmpid(EMPLOYEE1ID);
        e1.setName("Gilgamesh_1");
        e2.setEmpid(EMPLOYEE2ID);
        e2.setName("Enkidu_1");

        e1.getDepartments().add(sales);
        e2.getDepartments().add(sales);
        sales.getEmployees().add(e1);
        sales.getEmployees().add(e2);

        em.persist(e1);
        em.persist(e2);
        em.persist(sales);
        em.persist(marketing);
        em.flush();
        em.getTransaction().commit();
        em.close();

    }

    public void testNonRelationalFieldInverseSideVersionUpdate() {
        EntityManager em = emf.createEntityManager();

        M2MBiDepartment sales = em.find(M2MBiDepartment.class, SALESID);
        M2MBiEmployee e1 = em.find(M2MBiEmployee.class, EMPLOYEE1ID);
        M2MBiEmployee e2 = em.find(M2MBiEmployee.class, EMPLOYEE2ID);

        int salesVersionPre = sales.getVersion();
        int e1VersionPre = e1.getVersion();
        int e2VersionPre = e2.getVersion();

        em.getTransaction().begin();
        sales.setCostCode("1001");
        em.getTransaction().commit();
        em.close();

        em = emf.createEntityManager();
        sales = em.find(M2MBiDepartment.class, SALESID);
        e1 = em.find(M2MBiEmployee.class, EMPLOYEE1ID);
        e2 = em.find(M2MBiEmployee.class, EMPLOYEE2ID);

        int salesVersionPost = sales.getVersion();
        int e1VersionPost = e1.getVersion();
        int e2VersionPost = e2.getVersion();
        em.close();

        assertEquals(salesVersionPost, salesVersionPre + 1);
        assertEquals(e1VersionPost, e1VersionPre);
        assertEquals(e2VersionPost, e2VersionPre);

    }

    public void testNonRelationalFieldOwnerSideVersionUpdate() {
        // Change only non-relation fields on Employee.
        // Version number of Employee should be updated.
        // Version number of Department should not change.
        EntityManager em = emf.createEntityManager();
        M2MBiDepartment sales = em.find(M2MBiDepartment.class, SALESID);
        M2MBiEmployee e1 = em.find(M2MBiEmployee.class, EMPLOYEE1ID);
        M2MBiEmployee e2 = em.find(M2MBiEmployee.class, EMPLOYEE2ID);

        int salesVersionPre = sales.getVersion();
        int e1VersionPre = e1.getVersion();
        int e2VersionPre = e2.getVersion();

        em.getTransaction().begin();
        e1.setName("Gilgamesh_2");
        e2.setName("Enkidu_2");
        em.getTransaction().commit();
        em.close();

        em = emf.createEntityManager();
        sales = em.find(M2MBiDepartment.class, SALESID);
        e1 = em.find(M2MBiEmployee.class, EMPLOYEE1ID);
        e2 = em.find(M2MBiEmployee.class, EMPLOYEE2ID);

        int salesVersionPost = sales.getVersion();
        int e1VersionPost = e1.getVersion();
        int e2VersionPost = e2.getVersion();
        em.close();

        assertEquals(salesVersionPost, salesVersionPre);
        assertEquals(e1VersionPost, e1VersionPre + 1);
        assertEquals(e2VersionPost, e2VersionPre + 1);
    }


    public void testRelationalFieldBothSidesVersionUpdate() {
        // Move Employee from old Department to new Department.
        // Update both sides of the relationship.
        // Since there is a bidirectional ManyToMany relationship
        // from  Employee to Department, Employee version should
        // be updated. Since neither the new nor the old Departments
        // are owners of the reassigned Employee, the Department
        // versions should remain the same.

        EntityManager em = emf.createEntityManager();
        M2MBiDepartment sales = em.find(M2MBiDepartment.class, SALESID);
        M2MBiDepartment marketing = em.find(M2MBiDepartment.class, MARKETINGID);
        M2MBiEmployee e1 = em.find(M2MBiEmployee.class, EMPLOYEE1ID);
        M2MBiEmployee e2 = em.find(M2MBiEmployee.class, EMPLOYEE2ID);

        int salesVersionPre = sales.getVersion();
        int marketingVersionPre = marketing.getVersion();
        int e1VersionPre = e1.getVersion();
        int e2VersionPre = e2.getVersion();

        em.getTransaction().begin();
        // Remove sales from e1
        Collection<M2MBiDepartment> e1Departments = e1.getDepartments();
        for (Iterator<M2MBiDepartment> dIterator = e1Departments.iterator();
                dIterator.hasNext();) {
            M2MBiDepartment d = dIterator.next();
            if (SALESID.equals(d.getDeptid())) {
                dIterator.remove();
                break;
            }
        }
        // remove e1 from sales
        Collection<M2MBiEmployee> salesEmployees = sales.getEmployees();
        for (Iterator<M2MBiEmployee> eIterator = salesEmployees.iterator();
                eIterator.hasNext();) {
            M2MBiEmployee e = eIterator.next();
            if (EMPLOYEE1ID.equals(e.getEmpid())) {
                eIterator.remove();
                break;
            }
        }

        // Add marketing to e1
        e1.getDepartments().add(marketing);
        // Add e1 to marketing
        marketing.getEmployees().add(e1);

        em.getTransaction().commit();
        em.close();

        em = emf.createEntityManager();
        sales = em.find(M2MBiDepartment.class, SALESID);
        marketing = em.find(M2MBiDepartment.class, MARKETINGID);
        e1 = em.find(M2MBiEmployee.class, EMPLOYEE1ID);
        e2 = em.find(M2MBiEmployee.class, EMPLOYEE2ID);

        int salesVersionPost = sales.getVersion();
        int marketingVersionPost = marketing.getVersion();
        int e1VersionPost = e1.getVersion();
        int e2VersionPost = e2.getVersion();
        em.close();

        // Since Department is inverse side, there should
        // be no version update when its employees are moved.
        assertEquals(salesVersionPost, salesVersionPre);
        assertEquals(marketingVersionPost, marketingVersionPre);
        // Employee e1 was moved to marketing.
        assertEquals(e1VersionPost, e1VersionPre + 1);
        // Employee e2 was unchanged.
        assertEquals(e2VersionPost, e2VersionPre);
    }
}
