/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.testing.tests.unitofwork;

import org.eclipse.persistence.annotations.IdValidation;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.queries.ReadObjectQuery;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.sessions.UnitOfWork;
import org.eclipse.persistence.testing.framework.AutoVerifyTestCase;
import org.eclipse.persistence.testing.framework.TestErrorException;
import org.eclipse.persistence.testing.framework.TestProblemException;
import org.eclipse.persistence.testing.framework.TestWarningException;


/**
 * This test is in response to a support question.  There was a problem with
 * The stability of the cache when an object was registered with the Unit Of
 * Work as a new object.
 *
 * Test extended for bug Bug 300556 - Can't configure descriptor to make sequence override existing negative pk values.
 * Test can run with different IdValidations:
 *   IdValidation.NULL: Allow 0 or negative primary key (now not supported by default).
 *   IdValidation.ZERO: Allow negative primary key (default).
 *   IdValidation.NEGATIVE: Allow only positive primary key (now not supported by default).
 * keepSequencing set to true indicates that sequence values should be assigned to the newly created objects:
 *   if shouldAlwaysOverrideExistingValue is true then all existing pk values should be overridden by sequencing,
 *   otherwise only not allowed pk values should be overridden.
 */

public class RegisterNewObjectInIdentityMapNoSeqTest extends AutoVerifyTestCase {
    public String sequenceNumberName;
    public DatabaseField sequenceNumberField;
    boolean keepSequencing;
    boolean shouldAlwaysOverrideExistingValue;
    IdValidation idValidation;
    boolean zeroFailed;
    boolean negativeFailed;
    boolean zeroOverridden;
    boolean negativeOverridden;
    IdValidation idValidationOriginal;
    boolean shouldAlwaysOverrideExistingValueOriginal;

    public RegisterNewObjectInIdentityMapNoSeqTest(IdValidation idValidation, boolean keepSequencing, boolean shouldAlwaysOverrideExistingValue) {
        if(idValidation != IdValidation.NULL && idValidation != IdValidation.ZERO && idValidation != IdValidation.NEGATIVE) {
            throw new TestProblemException(idValidation + " is not supported.");
        }
        this.idValidation = idValidation;
        this.keepSequencing = keepSequencing;
        if(keepSequencing) {
            this.shouldAlwaysOverrideExistingValue = shouldAlwaysOverrideExistingValue;
        }
        setDescription("This test verifies the the UOW cache when registering a new object with a primitive primary key");
        setName(getName() + getNameSuffix());
    }

    String getNameSuffix() {
        return " " + idValidation + (keepSequencing ? " Sequencing" + (shouldAlwaysOverrideExistingValue ? " always overrides" : ""): "");
    }

    public void reset() {
        ClassDescriptor descriptor = getSession().getClassDescriptor(Weather.class);
        descriptor.setIdValidation(idValidationOriginal);
        if(keepSequencing) {
            // ReturningPolicyTestModel substitutes sequences for returning
            if(descriptor.getSequence() != null) {
                descriptor.getSequence().setShouldAlwaysOverrideExistingValue(shouldAlwaysOverrideExistingValueOriginal);
            }
        } else {
            descriptor.setSequenceNumberField(this.sequenceNumberField);
            descriptor.setSequenceNumberName(this.sequenceNumberName);
        }
        getAbstractSession().rollbackTransaction();
        getSession().getIdentityMapAccessor().initializeAllIdentityMaps();
    }

    public void setup() {
        zeroFailed = false;
        negativeFailed = false;
        zeroOverridden = false;
        negativeOverridden = false;

        getAbstractSession().beginTransaction();
        ClassDescriptor descriptor = getSession().getClassDescriptor(Weather.class);
        idValidationOriginal = descriptor.getIdValidation();
        descriptor.setIdValidation(idValidation);
        if(keepSequencing) {
            if(descriptor.getSequence() == null) {
                // ReturningPolicyTestModel substitutes sequences for returning
                throw new TestWarningException("Cannot run test with descriptor.getSequence() == null");
            }
            if(!shouldAlwaysOverrideExistingValue) {
                if(descriptor.getSequence().shouldAcquireValueAfterInsert()) {
                    throw new TestProblemException("Cannot run test with keepSequencing==true and alwayOverrideExistingValueOriginal==false with Identity sequence: it should always override");
                }
            }
            shouldAlwaysOverrideExistingValueOriginal = descriptor.getSequence().shouldAlwaysOverrideExistingValue();
            descriptor.getSequence().setShouldAlwaysOverrideExistingValue(shouldAlwaysOverrideExistingValue);
        } else {
            sequenceNumberField = descriptor.getSequenceNumberField();
            descriptor.setSequenceNumberField(null);
            sequenceNumberName = descriptor.getSequenceNumberName();
            descriptor.setSequenceNumberName(null);
        }
    }

    public void test() {
        Session session = getSession();
        UnitOfWork uow = session.acquireUnitOfWork();
        uow.setShouldNewObjectsBeCached(true);

        Weather weather = new Weather();
        weather.setStormPattern("Something really bad");
        weather.id = 0;
        ReadObjectQuery query = new ReadObjectQuery(weather);
        query.checkCacheOnly();
        Weather weatherClone = (Weather)uow.registerObject(weather);
        weather = (Weather)uow.executeQuery(query);
        zeroFailed = weather == null;

        Weather weatherNeg = new Weather();
        weatherNeg.setStormPattern("Something really bad below zero");
        weatherNeg.id = -1;
        ReadObjectQuery queryNeg = new ReadObjectQuery(weatherNeg);
        queryNeg.checkCacheOnly();
        Weather weatherNegClone = (Weather)uow.registerObject(weatherNeg);
        weatherNeg = (Weather)uow.executeQuery(queryNeg);
        negativeFailed = weatherNeg == null;

        if(keepSequencing) {
            uow.assignSequenceNumbers();
            zeroOverridden = weatherClone.id != 0;
            negativeOverridden = weatherNegClone.id != -1;
        }
    }

    public void verify() {
        String errorMsg = "";
        boolean zeroFailedExpected = false;
        boolean negativeFailedExpected = false;
        boolean zeroOverriddenExpected = shouldAlwaysOverrideExistingValue;
        boolean negativeOverriddenExpected = shouldAlwaysOverrideExistingValue;
        if(idValidation == IdValidation.NULL) {
            // nothing to do
        } else if(idValidation == IdValidation.ZERO) {
            zeroFailedExpected = true;
            negativeFailedExpected = false;
            if(keepSequencing) {
                zeroOverriddenExpected = true;
                negativeOverriddenExpected = shouldAlwaysOverrideExistingValue;
            }
        } else if(idValidation == IdValidation.NEGATIVE) {
            zeroFailedExpected = true;
            negativeFailedExpected = true;
            if(keepSequencing) {
                zeroOverriddenExpected = true;
                negativeOverriddenExpected = true;
            }
        }
        if(zeroFailed != zeroFailedExpected) {
            errorMsg += " zeroFailed = " + zeroFailed + ";";
        }
        if(negativeFailed != negativeFailedExpected) {
            errorMsg += " negativeFailed = " + negativeFailed + ";";
        }
        if(keepSequencing) {
            if(zeroOverridden != zeroOverriddenExpected) {
                errorMsg += " zeroOverridden = " + zeroOverridden + ";";
            }
            if(negativeOverridden != negativeOverriddenExpected) {
                errorMsg += " negativeOverridden = " + negativeOverridden + ";";
            }
        }
        if (errorMsg.length() > 0) {
            errorMsg = getNameSuffix() + ": Unexpected results: " + errorMsg;
            throw new TestErrorException(errorMsg);
        }
    }
}
