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

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import jakarta.persistence.EntityManager;

import org.apache.openjpa.conf.Compatibility;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.openjpa.persistence.proxy.entities.Address;
import org.apache.openjpa.persistence.proxy.entities.Annuity;
import org.apache.openjpa.persistence.proxy.entities.AnnuityHolder;
import org.apache.openjpa.persistence.proxy.entities.AnnuityHolderCategory;
import org.apache.openjpa.persistence.proxy.entities.AnnuityPersistebleObject;
import org.apache.openjpa.persistence.proxy.entities.AnnuityType;
import org.apache.openjpa.persistence.proxy.entities.Contact;
import org.apache.openjpa.persistence.proxy.entities.ContactType;
import org.apache.openjpa.persistence.proxy.entities.EquityAnnuity;
import org.apache.openjpa.persistence.proxy.entities.FixedAnnuity;
import org.apache.openjpa.persistence.proxy.entities.IAnnuity;
import org.apache.openjpa.persistence.proxy.entities.IAnnuityHolder;
import org.apache.openjpa.persistence.proxy.entities.IContact;
import org.apache.openjpa.persistence.proxy.entities.IEquityAnnuity;
import org.apache.openjpa.persistence.proxy.entities.IFixedAnnuity;
import org.apache.openjpa.persistence.proxy.entities.IPayor;
import org.apache.openjpa.persistence.proxy.entities.IPayout;
import org.apache.openjpa.persistence.proxy.entities.IRider;
import org.apache.openjpa.persistence.proxy.entities.Payor;
import org.apache.openjpa.persistence.proxy.entities.Payout;
import org.apache.openjpa.persistence.proxy.entities.Person;
import org.apache.openjpa.persistence.proxy.entities.Rider;
import org.apache.openjpa.persistence.proxy.entities.RiderType;
import org.apache.openjpa.persistence.test.SingleEMFTestCase;


/*
 * Test the complicated interaction between Detached entities, Proxy classes
 * and Merging changes made in ProxyCollections back into entities.
 *
 * This code is based on AcmeTest2, which was originally written by
 * Mohammad at IBM and contributed under ASL 2.0.
 */
public class TestDetachMerge extends SingleEMFTestCase {

    @Override
    public void setUp() {
        setUp(DROP_TABLES, Address.class, Annuity.class, AnnuityHolder.class, AnnuityPersistebleObject.class,
            Contact.class, EquityAnnuity.class, FixedAnnuity.class, Payor.class, Payout.class,
            Person.class, Rider.class);
    }

    /*
     * Test default 1.0 compatibility behavior, which should pass AS-IS
     */
    public void testAnnuity1Compat() throws Exception {
        OpenJPAEntityManagerFactorySPI emf1 =
            (OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.createEntityManagerFactory(
            "Annuity1Compat", "org/apache/openjpa/persistence/proxy/persistence1.xml");
        assertNotNull(emf1);

        Log log = emf1.getConfiguration().getLog("test");

        if (log.isTraceEnabled()) {
            Compatibility compat = emf1.getConfiguration().getCompatibilityInstance();
            assertNotNull(compat);
            log.trace("started testAnnuity1Compat()");
            log.trace("FlushBeforeDetach=" + compat.getFlushBeforeDetach());
            log.trace("CopyOnDetach=" + compat.getCopyOnDetach());
            log.trace("CascadeWithDetach=" + compat.getCascadeWithDetach());
            log.trace("IgnoreDetachedStateFieldForProxySerialization=" +
                compat.getIgnoreDetachedStateFieldForProxySerialization());
        }

        try {
            execute(emf1);
        } catch (RuntimeException e) {
            fail("testAnuity1Compat() should not have caused an execption!" + e);
        } finally {
            emf1.close();
        }
    }

    /*
     * Test default 2.0 compatibility behavior, which should PASS
     */
    public void testAnnuity2Compat() throws Exception {
        OpenJPAEntityManagerFactorySPI emf2 =
            (OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.createEntityManagerFactory(
            "Annuity2Compat", "org/apache/openjpa/persistence/proxy/persistence2.xml");
        assertNotNull(emf2);

        Log log = emf2.getConfiguration().getLog("test");

        if (log.isTraceEnabled()) {
            Compatibility compat = emf2.getConfiguration().getCompatibilityInstance();
            assertNotNull(compat);
            log.trace("started testAnnuity2Compat()");
            log.trace("FlushBeforeDetach=" + compat.getFlushBeforeDetach());
            log.trace("CopyOnDetach=" + compat.getCopyOnDetach());
            log.trace("CascadeWithDetach=" + compat.getCascadeWithDetach());
            log.trace("IgnoreDetachedStateFieldForProxySerialization=" +
                compat.getIgnoreDetachedStateFieldForProxySerialization());
        }

        try {
            execute(emf2);
        } catch (RuntimeException e) {
            fail("testAnuity2Compat() should not have caused an execption!" + e);
        } finally {
            emf2.close();
        }
    }

    private void execute(OpenJPAEntityManagerFactorySPI myEMF) throws Exception {
        Log log = myEMF.getConfiguration().getLog("test");
        //EntityManager em = myEMF.createEntityManager();
        IContact contact = null;

        try {
            if (log.isTraceEnabled())
                log.trace("creating contact");
            try {
                contact = createContact(myEMF);
            } catch (Exception e) {
                log.error("Create Contact failed.", e);
                throw e;
            }

            try {
                verifyContactValues(myEMF, contact);
            } catch (Exception e) {
                log.error("Create Contact verification failed.", e);
                throw e;
                // do not return, as this might be a small bug that we can bypass
            }

            if (log.isTraceEnabled())
                log.trace("creating annuity holder");
            IAnnuityHolder annuityHolder = null;
            try {
                annuityHolder = createAnnuityHolder(myEMF, contact);
            } catch (Exception e) {
                log.error("failed to create Annuity Holder Successfully.", e);
                throw e;
            }

            try {
                verifyAnnuityHolderValues(myEMF, annuityHolder);
            } catch (Exception e) {
                log.info("failed to verify create annuity holder successfuly.", e);
                throw e;
            }

            if (log.isTraceEnabled())
                log.trace("creating payor");
            IPayor payor = null;
            try {
                payor = createPayor(myEMF);
            } catch(Exception e) {
                log.error("failed to create payor successfuly.", e);
                throw e;
            }

            try {
                verifyPayorValues(myEMF, payor);
            } catch (Exception e) {
                log.error("failed to verify create payor successfuly.", e);
                throw e;
            }

            if (log.isTraceEnabled())
                log.trace("creating annuity");
            IAnnuity annuity =null;
            AnnuityType annuityType = AnnuityType.FIXED;
            try {
                annuity = createAnnuityWithRider(myEMF, annuityType);
            } catch (Exception e) {
                log.error("failed to create annuity successfuly.", e);
                throw e;
            }

            try {
                log.trace("verify annuity with rider");
                verifyAnnuityValues(myEMF, annuity, annuityType);
            } catch (Exception e) {
                log.error("failed to verify create annuity successfuly.", e);
                throw e;
            }

            if (log.isTraceEnabled())
                log.trace("upating annuity");
            try {
                log.trace("create annuity with payout");
                annuity = createAnnuityPayout(myEMF, annuity);
            } catch (Exception e) {
                log.error("failed to create annuity successfuly.", e);
                throw e;
            }

            try {
                verifyAnnuityValues(myEMF, annuity, annuityType);
            } catch (Exception e) {
                log.error("failed to verify create annuity successfuly.", e);
                throw e;
            }

            if (log.isTraceEnabled())
                log.trace("upating annuity");
            try {
                EntityManager em = createEM(myEMF);
                em.getTransaction().begin();
                    annuity.getPayors().add(payor);
                    annuity.setAnnuityHolderId(annuityHolder.getId());
                    annuity = em.merge(annuity);
                em.getTransaction().commit();
                closeEM(em);
            } catch (Exception e) {
                log.error("failed to update annuity successfuly.", e);
                throw e;
            }

            try {
                verifyAnnuityValues(myEMF, annuity, annuityType);
            } catch (Exception e) {
                log.error("failed to verify annuity update successfuly.", e);
                throw e;
            }
        } finally {
            log.error("scenario: failed.");
        }

        if (log.isTraceEnabled())
            log.trace("scenario: completed.");
    }

    private IAnnuity createAnnuityPayout(OpenJPAEntityManagerFactorySPI myEMF, IAnnuity annuity) {
        EntityManager em = createEM(myEMF);
        em.getTransaction().begin();
        IPayout payout = new Payout();
        payout.setAnnuity(annuity);
        payout.setTaxableAmount(new BigDecimal(100.00));
        payout.setStartDate(Calendar.getInstance());
        payout.setEndDate(Calendar.getInstance());
        payout.setId(getId());
        em.persist(payout);
        em.getTransaction().commit();
        em.getTransaction().begin();
        annuity.getPayouts().add(payout);
        em.getTransaction().commit();
        closeEM(em);
        return annuity;
    }

    private IAnnuity createAnnuityWithRider(OpenJPAEntityManagerFactorySPI myEMF, AnnuityType annuityType) {
        EntityManager em = createEM(myEMF);
        em.getTransaction().begin();
        IAnnuity annuity = createAnnuity(annuityType);
        IRider rider1 = getRider();
        IRider rider2 = getRider();
        IRider rider3 = getRider();
        annuity.getRiders().add(rider1);
        annuity.getRiders().add(rider2);
        annuity.getRiders().add(rider3);
        em.persist(annuity);
        em.getTransaction().commit();
        closeEM(em);
        return annuity;
    }

    private IAnnuity createAnnuity(AnnuityType annuityType) {
        if (AnnuityType.BASIC.equals(annuityType)) {
            Annuity annuity = new Annuity();
            annuity.setId(getId());
            annuity.setAmount(500.00);
            annuity.setAccountNumber("123456");
            return annuity;
        }
        if (AnnuityType.EQUITY.equals(annuityType)) {
            EquityAnnuity annuity = new EquityAnnuity();
            annuity.setId(getId());
            annuity.setAmount(500.00);
            annuity.setAccountNumber("123456");
            annuity.setFundNames("Something nothing wrong");
            annuity.setIndexRate(10.99);
            annuity.setLastPaidAmt(100.00);
            return annuity;
        }
        if (AnnuityType.FIXED.equals(annuityType)) {
            FixedAnnuity annuity = new FixedAnnuity();
            ((FixedAnnuity)annuity).setRate(10.0);
            annuity.setId(getId());
            annuity.setAmount(500.00);
            annuity.setAccountNumber("123456");
            return annuity;
        }
        return null;
    }

    private IRider getRider() {
        IRider rider = new Rider();
        rider.setId(getId());
        rider.setRule("Pay");
        rider.setType(RiderType.REPLACE);
        rider.setEffectiveDate(new Date());
        return rider;
    }

    private void verifyAnnuityValues(OpenJPAEntityManagerFactorySPI myEMF, IAnnuity annuity, AnnuityType annuityType)
    throws Exception {
        IAnnuity results = findAnnuityById(myEMF, Annuity.class, annuity.getId());
        if (annuity instanceof IFixedAnnuity) {
            assertEqual((IFixedAnnuity)annuity, (IFixedAnnuity)results,
                "Fixed Annuity from Client is not equal to DB value", "Mismacth was found.");
        } else if (annuity instanceof IEquityAnnuity) {
            assertEqual((IEquityAnnuity)annuity, (IEquityAnnuity)results,
                    "Equity Annuity from Client is not equal to DB value", "Mismacth was found.");
        } else {
            assertEqual(annuity, results,
                    "Basic Annuity from Client is not equal to DB value", "Mismacth was found.");
        }

        assertEqual(annuity.getPayouts(), results.getPayouts(),
                "Annuity payouts from Client is not equal to DB value", "Mismacth was found in number of payouts");
        boolean found = false;
        if (annuity.getPayouts() != null) {
            IPayout clientPayout = null;
            for (int i=0; i<annuity.getPayouts().size(); i++) {
                found = false;  // reset
                clientPayout = annuity.getPayouts().get(i);
                for (IPayout resultPayout: results.getPayouts()) {
                    if (clientPayout.getId().equals(resultPayout.getId())){
                        found = true;
                        assertEqual(clientPayout, resultPayout,
                        "Annuity Payout from Client is not equal to DB value at location: " +i , "Mismacth was found");
                    }else{
                        continue;
                    }
                }
                if (!(found) && clientPayout != null) {
                    throw new RuntimeException("Annuity: Payout from client is not equal to DB.  " +
                            "Found Payout with id: " + clientPayout.getId() +
                            " on the client side, but not in the database for annuity id:" + annuity.getId());

                }
            }
        }

        assertRidersEqual(annuity.getRiders(), results.getRiders(),
                "Annuity rider from Client is not equal to DB value", "Mismacth was found in number of rider");
        if (annuity.getRiders() != null) {
            IRider clientRider = null;
            for (int i=0; i<annuity.getRiders().size(); i++) {
                found = false;  // reset
                clientRider = annuity.getRiders().get(i);
                for (IRider resultRider : results.getRiders()) {
                    if (clientRider.getId().equals(resultRider.getId())){
                        found = true;
                        assertEqual(clientRider, resultRider,
                        "Annuity rider from Client is not equal to DB value at location: " +i , "Mismacth was found");
                    }else{
                        continue;
                    }
                }
                if (!(found) && clientRider != null) {
                    throw new RuntimeException("Annuity: Rider from client is not equal to DB.  " +
                            "Found rider with id: " + clientRider.getId() +
                            " on the client side, but not in the database for annuity id:" + annuity.getId());

                }
            }
        }

        assertPayorsEqual(annuity.getPayors(), results.getPayors(),
                "Annuity Payor from Client is not equal to DB value", "Mismacth was found.");
        if (annuity.getPayors() != null) {
            IPayor clientPayor = null;
            for (int i=0; i<annuity.getPayors().size(); i++) {
                found = false;  // reset
                clientPayor = annuity.getPayors().get(i);
                for (IPayor resultPayor : results.getPayors()) {
                    if (clientPayor.getId().equals(resultPayor.getId())){
                        found = true;
                        assertEqual(annuity.getPayors().get(i), resultPayor,
                        "Annuity payor from Client is not equal to DB value at location: " +i , "Mismacth was found");
                    }else{
                        continue;
                    }
                }
                if (!(found) && clientPayor != null) {
                    throw new RuntimeException("Annuity: Payor from client is not equal to DB.  " +
                            "Found payor with id: " + clientPayor.getId() +
                            " on the client side, but not in the database for annuity id:" + annuity.getId());

                }
            }
        }
    }

    private void assertEqual(IAnnuity annuity, IAnnuity results, String string, String string2) throws Exception {
        if(annuity == null && results == null)
            return;
        if (annuity == null)
            throw new RuntimeException("Annuity: Annuities ! equal (Annuity was null).");
        if (!annuity.getId().equals(results.getId())) {
            throw new RuntimeException("Annuity: Annuities ! equal (Annuity ids not the same).");
        }
        assertPayorsEqual(annuity.getPayors(), results.getPayors(), string, string2);
        assertRidersEqual(annuity.getRiders(), results.getRiders(), string, string2);
        assertEqual(annuity.getPayouts(),results.getPayouts(), string, string2);
    }

    private void assertEqual(IEquityAnnuity annuity, IEquityAnnuity results, String string, String string2)
    throws Exception {
        if(annuity == null && results == null)
            return;
        if (annuity == null)
            throw new RuntimeException("Annuity: Annuities ! equal (EquityAnnuity was null).");
        if (!annuity.getId().equals(results.getId())) {
            throw new RuntimeException("Annuity: Annuities ! equal (EquityAnnuity ids not the same).");
        }

        assertPayorsEqual(annuity.getPayors(), results.getPayors(), string, string2);
        assertRidersEqual(annuity.getRiders(), results.getRiders(), string, string2);
        assertEqual(annuity.getPayouts(),results.getPayouts(), string, string2);
    }

    private void assertEqual(IFixedAnnuity annuity, IFixedAnnuity results, String string, String string2)
    throws Exception {
        if(annuity == null && results == null)
            return;
        if (annuity == null)
            throw new RuntimeException("Annuity: Annuities ! equal (FixedAnnuity was null).");
        if (!annuity.getId().equals(results.getId())) {
            throw new RuntimeException("Annuity: Annuities ! equal (FixedAnnuity ids not the same).");
        }
        assertPayorsEqual(annuity.getPayors(), results.getPayors(), string, string2);
        assertRidersEqual(annuity.getRiders(), results.getRiders(), string, string2);
        assertEqual(annuity.getPayouts(),results.getPayouts(), string, string2);
    }

    private IAnnuity findAnnuityById(OpenJPAEntityManagerFactorySPI myEMF, Class<Annuity> class1, String id) {
        EntityManager em = createEM(myEMF);
        IAnnuity ann = em.find(class1, id);
        closeEM(em);
        return ann;
    }

    private void assertEqual(List<IPayout> payouts, List<IPayout> payouts2, String string, String string2)
    throws Exception {
        if (payouts == null && payouts2 == null)
            return;
        if (payouts == null)
            throw new RuntimeException("Annuity: IPayout list not the same (payouts was null)!");
        if (payouts.size() != payouts2.size())
            throw new RuntimeException("Annuity: IPayout list not the same (payouts size not the same)!");
        for (int i = 0; i < payouts.size(); i++) {
            IPayout payout = payouts.get(i);
            boolean found = false;
            for (int j = 0; i < payouts2.size(); j++) {
                try {
                    assertEqual(payout, payouts2.get(j), string, string2);
                    found = true;
                    break;
                } catch (Exception e) {
                    continue;
                }
            }
            if (!found) {
                throw new RuntimeException("Annuity: IPayout list not the same (no match found)!");
            }
        }
    }

    private void assertEqual(IPayout clientPayout, IPayout resultPayout, String string, String string2)
    throws Exception {
        if (clientPayout == null && resultPayout == null)
            return;
        if (clientPayout == null)
            throw new RuntimeException("Annuity: IPayout not the same (clientPayout was null)! " +
                string + " " + string2);
        if (clientPayout.getId().equals(resultPayout.getId()))
            return;
        throw new RuntimeException("Annuity: IPayout not the same (clientPayout ids not the same)! " +
            string + " " + string2);
    }

    private void assertRidersEqual(List<IRider> riders, List<IRider> riders2, String string, String string2)
    throws Exception {
        if (riders == null && riders2 == null)
            return;
        if (riders == null)
            throw new RuntimeException("Annuity: IRider list not the same (riders was null)!");
        if (riders.size() != riders2.size())
            throw new RuntimeException("Annuity: IRider list not the same (riders size not the same)!");
        for (int i = 0; i < riders.size(); i++) {
            IRider rider = riders.get(i);
            boolean found = false;
            for (int j = 0; i < riders2.size(); j++) {
                try {
                    assertEqual(rider, riders2.get(j), string, string2);
                    found = true;
                    break;
                } catch (Exception e) {
                    continue;
                }
            }
            if (!found) {
                throw new RuntimeException("Annuity: IRider list not the same (match not found)!");
            }
        }
    }

    private void assertEqual(IRider clientRider, IRider resultRider, String string, String string2) throws Exception {
        if (clientRider == null && resultRider == null)
            return;
        if (clientRider == null)
            throw new RuntimeException("Annuity: IRider not the same (clientRider was null)! " +
                string + " " + string2);
        if (clientRider.getId().equals(resultRider.getId()))
            return;
        throw new RuntimeException("Annuity: IRider not the same (no match found)! " +
            string + " " + string2);
    }

    private void assertPayorsEqual(List<IPayor> payors, List<IPayor> payors2, String string, String string2)
    throws Exception {
        if (payors == null && payors2 == null)
            return;
        if (payors == null)
            throw new RuntimeException("Annuity: IPayor list not the same (payors was null)!");
        if (payors.size() != payors2.size())
            throw new RuntimeException("Annuity: IPayor list not the same (payors size not the same)! payors=" +
                    Arrays.toString(payors.toArray()) + ", payors2=" + payors2.toString());
        for (int i = 0; i < payors.size(); i++) {
            IPayor payor = payors.get(i);
            boolean found = false;
            for (int j = 0; i < payors2.size(); j++) {
                try {
                    assertEqual(payor, payors2.get(j), string, string2);
                    found = true;
                    break;
                } catch (Exception e) {
                    continue;
                }
            }
            if (!found) {
                throw new RuntimeException("Annuity: IPayor list not the same (no match found)!");
            }
        }
    }

    private void verifyPayorValues(OpenJPAEntityManagerFactorySPI myEMF, IPayor payor) throws Exception {
        IPayor results = null;
        results = findPayorById(myEMF, Payor.class, payor.getId());
        assertEqual(payor, results,
                "Payor from Client is not equal to DB value.", "Mismacth was found.");
    }

    private IPayor findPayorById(OpenJPAEntityManagerFactorySPI myEMF, Class<Payor> class1, String id) {
        EntityManager em = createEM(myEMF);
        IPayor ip = em.find(class1, id);
        closeEM(em);
        return ip;
    }

    private void assertEqual(IPayor payor, IPayor results, String string, String string2) throws Exception {
        if (payor == null && results == null)
            return;
        if (payor == null)
            throw new RuntimeException("Annuity: IPayor not the same (payor was null)! " +
                string + " " + string2);
        if (payor.getId().equals(results.getId()))
            return;
        throw new RuntimeException("Annuity: IPayor not the same (no match found)! " +
            string + " " + string2);
    }

    private IPayor createPayor(OpenJPAEntityManagerFactorySPI myEMF) {
        EntityManager em = createEM(myEMF);
        em.getTransaction().begin();
        IPayor payor = new Payor();
        payor.setId(getId());
        payor.setName("Payor");
        em.persist(payor);
        em.getTransaction().commit();
        closeEM(em);
        return payor;
    }

    private void verifyAnnuityHolderValues(OpenJPAEntityManagerFactorySPI myEMF, IAnnuityHolder annuityHolder)
    throws Exception {
        IAnnuityHolder result = null;
        result = findHolderById(myEMF, AnnuityHolder.class, annuityHolder.getId());
        assertEqual(annuityHolder, result,
            "Annuity Holder from Client is not equal to DB value.", "Mismacth was found.");
        assertEqual(annuityHolder.getContact(), result.getContact(),
                "Annuity Holder Contact from Client is not equal to DB value.", "Mismacth was found.");
    }

    private IAnnuityHolder findHolderById(OpenJPAEntityManagerFactorySPI myEMF, Class<AnnuityHolder> class1, String id)
    {
        EntityManager em = createEM(myEMF);
        IAnnuityHolder result = em.find(class1, id);
        closeEM(em);
        return result;
    }

    private EntityManager createEM(OpenJPAEntityManagerFactorySPI myEMF) {
        return myEMF.createEntityManager();
    }

    private void assertEqual(IAnnuityHolder annuityHolder, IAnnuityHolder results, String string, String string2)
    throws Exception {
            if (annuityHolder == null && results == null)
                return;
            if (annuityHolder == null)
                throw new RuntimeException("Annuity: IAnnuityHolder not the same (annuityHolder was null)! " +
                    string + " " + string2);
            if (annuityHolder.getId().equals(results.getId()))
                return;
            throw new RuntimeException("Annuity: IAnnuityHolder not the same (no match found)! " +
                string + " " + string2);
    }

    private void assertEqual(IContact contact, IContact contact2, String string, String string2) throws Exception {
        if (contact == null && contact2 == null)
            return;
        if (contact == null)
            throw new RuntimeException("Annuity: Contacts not the same (contact was null)! " +
                string + " " + string2);
        if (contact.getId().equals(contact2.getId()))
            return;
        throw new RuntimeException("Annuity: Contacts not the same (no match found)! " +
            string + " " + string2);
    }

    private IAnnuityHolder createAnnuityHolder(OpenJPAEntityManagerFactorySPI myEMF, IContact contact) {
        EntityManager em = createEM(myEMF);
        em.getTransaction().begin();
        IAnnuityHolder annuityHolder = new AnnuityHolder();
        annuityHolder.setCategory(AnnuityHolderCategory.METAL);
        annuityHolder.setContact(contact);
        annuityHolder.setId(getId());
        annuityHolder.setFirstName("bob");
        annuityHolder.setDateOfBirth(new Date());
        annuityHolder.setGovernmentId("US");
        annuityHolder.setLastName("dog");
        annuityHolder.setTimeOfBirth(new Date());
        em.persist(annuityHolder);
        em.getTransaction().commit();
        closeEM(em);
        return annuityHolder;
    }

    private void verifyContactValues(OpenJPAEntityManagerFactorySPI myEMF, IContact contact) throws Exception {
        // read the contact with id.
        IContact results = null;
        results = findContactById(myEMF, Contact.class, contact.getId());
        assertEqual(contact, results,
            "Contact from Client is not equal to DB value.", "Mismacth was found.");
    }

    private IContact findContactById(OpenJPAEntityManagerFactorySPI myEMF, Class<Contact> class1, String id) {
        EntityManager em = createEM(myEMF);
        IContact ic = em.find(class1, id);
        closeEM(em);
        return ic;
    }

    private IContact createContact(OpenJPAEntityManagerFactorySPI myEMF) {
        EntityManager em = createEM(myEMF);
        em.getTransaction().begin();
        IContact contact = null;
        contact = new Contact();
        contact.setContactType(ContactType.BUSINESS);
        contact.setId(getId());
        contact.setEmail("here@there");
        contact.setPhone("555-5555");
        em.persist(contact);
        em.getTransaction().commit();
        closeEM(em);
        return contact;
    }

    private String getId() {
        UUID uid = UUID.randomUUID();
        return uid.toString();
    }

}
