/*
 * This file is part of Bisq.
 *
 * Bisq is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * Bisq is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Bisq. If not, see <http://www.gnu.org/licenses/>.
 */

package haveno.apitest.scenario;

import haveno.apitest.method.offer.AbstractOfferTest;
import haveno.core.payment.PaymentAccount;
import haveno.proto.grpc.OfferInfo;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.condition.EnabledIf;

import static java.lang.System.getenv;
import static org.junit.jupiter.api.Assertions.fail;
import static protobuf.OfferDirection.BUY;
import static protobuf.OfferDirection.SELL;

/**
 * Used to verify trigger based, automatic offer deactivation works.
 * Disabled by default.
 * Set ENV or IDE-ENV LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED=true to run.
 */
@EnabledIf("envLongRunningTestEnabled")
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LongRunningOfferDeactivationTest extends AbstractOfferTest {

    private static final int MAX_ITERATIONS = 500;

    @Test
    @Order(1)
    public void testSellOfferAutoDisable(final TestInfo testInfo) {
        PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
        double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
        String triggerPrice = calcPriceAsString(mktPriceAsDouble, -50.0000, 4);
        log.info("Current USD mkt price = {}  Trigger Price = {}", mktPriceAsDouble, triggerPrice);
        OfferInfo offer = aliceClient.createMarketBasedPricedOffer(SELL.name(),
                "USD",
                1_000_000,
                1_000_000,
                0.00,
                defaultBuyerSecurityDepositPct.get(),
                paymentAcct.getId(),
                triggerPrice);
        log.info("SELL offer {} created with margin based price {}.",
                offer.getId(),
                offer.getPrice());
        genBtcBlocksThenWait(1, 2500);  // Wait for offer book entry.

        offer = aliceClient.getOffer(offer.getId()); // Offer has trigger price now.
        log.info("SELL offer should be automatically disabled when mkt price falls below {}.", offer.getTriggerPrice());

        int numIterations = 0;
        while (++numIterations < MAX_ITERATIONS) {
            offer = aliceClient.getOffer(offer.getId());

            var mktPrice = aliceClient.getBtcPrice("USD");
            if (offer.getIsActivated()) {
                log.info("Offer still enabled at mkt price {} > {} trigger price",
                        mktPrice,
                        offer.getTriggerPrice());
                sleep(1000 * 60); // 60s
            } else {
                log.info("Successful test completion after offer disabled at mkt price {} < {} trigger price.",
                        mktPrice,
                        offer.getTriggerPrice());
                break;
            }
            if (numIterations == MAX_ITERATIONS)
                fail("Offer never disabled");

            genBtcBlocksThenWait(1, 0);
        }
    }

    @Test
    @Order(2)
    public void testBuyOfferAutoDisable(final TestInfo testInfo) {
        PaymentAccount paymentAcct = createDummyF2FAccount(aliceClient, "US");
        double mktPriceAsDouble = aliceClient.getBtcPrice("USD");
        String triggerPrice = calcPriceAsString(mktPriceAsDouble, 50.0000, 4);
        log.info("Current USD mkt price = {}  Trigger Price = {}", mktPriceAsDouble, triggerPrice);
        OfferInfo offer = aliceClient.createMarketBasedPricedOffer(BUY.name(),
                "USD",
                1_000_000,
                1_000_000,
                0.00,
                defaultBuyerSecurityDepositPct.get(),
                paymentAcct.getId(),
                triggerPrice);
        log.info("BUY offer {} created with margin based price {}.",
                offer.getId(),
                offer.getPrice());
        genBtcBlocksThenWait(1, 2500);  // Wait for offer book entry.

        offer = aliceClient.getOffer(offer.getId()); // Offer has trigger price now.
        log.info("BUY offer should be automatically disabled when mkt price rises above {}.",
                offer.getTriggerPrice());

        int numIterations = 0;
        while (++numIterations < MAX_ITERATIONS) {
            offer = aliceClient.getOffer(offer.getId());

            var mktPrice = aliceClient.getBtcPrice("USD");
            if (offer.getIsActivated()) {
                log.info("Offer still enabled at mkt price {} < {} trigger price",
                        mktPrice,
                        offer.getTriggerPrice());
                sleep(1000 * 60); // 60s
            } else {
                log.info("Successful test completion after offer disabled at mkt price {} > {} trigger price.",
                        mktPrice,
                        offer.getTriggerPrice());
                break;
            }
            if (numIterations == MAX_ITERATIONS)
                fail("Offer never disabled");

            genBtcBlocksThenWait(1, 0);
        }
    }

    protected static boolean envLongRunningTestEnabled() {
        String envName = "LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED";
        String envX = getenv(envName);
        if (envX != null) {
            log.info("Enabled, found {}.", envName);
            return true;
        } else {
            log.info("Skipped, no environment variable {} defined.", envName);
            log.info("To enable on Mac OS or Linux:"
                    + "\tIf running in terminal, export LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED=true in bash shell."
                    + "\tIf running in Intellij, set LONG_RUNNING_OFFER_DEACTIVATION_TEST_ENABLED=true in launcher's Environment variables field.");
            return false;
        }
    }
}
