/*
 * 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 bisq.core.trade.bsq_swap;

import bisq.core.offer.Offer;
import bisq.core.offer.OpenOffer;
import bisq.core.offer.OpenOfferManager;
import bisq.core.provider.fee.FeeService;
import bisq.core.trade.protocol.bsq_swap.messages.BsqSwapRequest;
import bisq.core.util.Validator;
import bisq.core.util.coin.CoinUtil;

import bisq.network.p2p.NodeAddress;

import bisq.common.crypto.KeyRing;

import org.bitcoinj.core.Coin;

import java.util.Date;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.lang.Math.abs;

@Slf4j
public class BsqSwapTakeOfferRequestVerification {

    public static boolean isValid(OpenOfferManager openOfferManager,
                                  FeeService feeService,
                                  KeyRing keyRing,
                                  NodeAddress peer,
                                  BsqSwapRequest request) {
        try {
            log.info("Received {} from {} with tradeId {} and uid {}",
                    request.getClass().getSimpleName(), peer, request.getTradeId(), request.getUid());

            checkNotNull(request);
            Validator.nonEmptyStringOf(request.getTradeId());

            checkArgument(request.getSenderNodeAddress().equals(peer), "Node address not matching");

            Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(request.getTradeId());
            checkArgument(openOfferOptional.isPresent(), "Offer not found in open offers");

            OpenOffer openOffer = openOfferOptional.get();
            checkArgument(openOffer.getState() == OpenOffer.State.AVAILABLE, "Offer not available");

            Offer offer = openOffer.getOffer();
            Validator.checkTradeId(offer.getId(), request);
            checkArgument(offer.isMyOffer(keyRing), "Offer must be mine");

            long tradeAmount = request.getTradeAmount();
            Coin amountAsCoin = Coin.valueOf(request.getTradeAmount());

            checkArgument(tradeAmount >= offer.getMinAmount().getValue() &&
                    tradeAmount <= offer.getAmount().getValue(), "TradeAmount not within offers amount range");
            checkArgument(isDateInTolerance(request), "Trade date is out of tolerance");
            checkArgument(isTxFeeInTolerance(request, feeService), "Miner fee from taker not in tolerance");
            checkArgument(request.getMakerFee() == Objects.requireNonNull(CoinUtil.getMakerFee(false, amountAsCoin)).value);
            checkArgument(request.getTakerFee() == CoinUtil.getTakerFee(false, amountAsCoin).value);
        } catch (Exception e) {
            log.error("BsqSwapTakeOfferRequestVerification failed. Request={}, peer={}, error={}", request, peer, e.toString());
            return false;
        }

        return true;
    }

    private static boolean isDateInTolerance(BsqSwapRequest request) {
        return abs(request.getTradeDate() - new Date().getTime()) < TimeUnit.MINUTES.toMillis(10);
    }

    private static boolean isTxFeeInTolerance(BsqSwapRequest request, FeeService feeService) {
        double myFee = (double) feeService.getTxFeePerVbyte().getValue();
        double peersFee = (double) Coin.valueOf(request.getTxFeePerVbyte()).getValue();
        // Allow for 10% diff in mining fee, ie, maker will accept taker fee that's 10%
        // off their own fee from service. Both parties will use the same fee while
        // creating the bsq swap tx
        double diff = abs(1 - myFee / peersFee);
        boolean isInTolerance = diff < 0.5;
        if (!isInTolerance) {
            log.warn("Miner fee from taker not in tolerance. myFee={}, peersFee={}, diff={}", myFee, peersFee, diff);
        }
        return isInTolerance;
    }
}

@@ -3392,16 +3392,39 @@
 ee() == 
+Objects.requireNonNull(
 CoinUtil
@@ -3456,16 +3456,17 @@
 tAsCoin)
+)
 .value);
@@ -4147,17 +4147,17 @@
 low for 
-1
+5
 0%25 diff 
@@ -4214,11 +4214,12 @@
 t's 
-10%25
+less
 %0A   
@@ -4225,16 +4225,25 @@
       //
+ than 50%25
  off the
@@ -4265,17 +4265,64 @@
  service
-.
+ (that is, 100%25 higher or 50%25 lower).%0A        //
  Both pa
@@ -4354,27 +4354,16 @@
 ee while
-%0A        //
  creatin
@@ -4379,16 +4379,17 @@
  swap tx
+.
 %0A       

Fix misleading comment: BSQ swap taker tx fee tolerance