package ru.yandex.autotests.direct.api.keywordbids.setauto;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.Optional;
import java.util.stream.Collectors;

import com.yandex.direct.api.v5.keywordbids.AuctionBidItem;
import com.yandex.direct.api.v5.keywordbids.AuctionBids;
import com.yandex.direct.api.v5.keywordbids.GetResponse;
import com.yandex.direct.api.v5.keywordbids.KeywordBidFieldEnum;
import com.yandex.direct.api.v5.keywordbids.KeywordBidGetItem;
import com.yandex.direct.api.v5.keywordbids.KeywordBidSearchFieldEnum;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.aqua.annotations.project.Aqua;
import ru.yandex.autotests.direct.api.bids.BidsLogins;
import ru.yandex.autotests.direct.api.keywordbids.KeywordBidsFeatures;
import ru.yandex.autotests.direct.utils.money.Currency;
import ru.yandex.autotests.direct.utils.money.MoneyCurrency;
import ru.yandex.autotests.directapi.apiclient.config.ProtocolType;
import ru.yandex.autotests.directapi.apiclient.config.Semaphore;
import ru.yandex.autotests.directapi.model.api5.campaigns.TextCampaignNetworkStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.campaigns.TextCampaignSearchStrategyAddMap;
import ru.yandex.autotests.directapi.model.api5.keywordbids.GetRequestMap;
import ru.yandex.autotests.directapi.model.api5.keywordbids.KeywordBidExpectedResult;
import ru.yandex.autotests.directapi.model.api5.keywordbids.KeywordBidsSelectionCriteriaMap;
import ru.yandex.autotests.directapi.model.api5.keywordbids.KeywordBidsSetAutoItemMap;
import ru.yandex.autotests.directapi.model.api5.keywordbids.SearchByTrafficVolumeBiddingRule;
import ru.yandex.autotests.directapi.model.api5.keywordbids.SetAutoBiddingRule;
import ru.yandex.autotests.directapi.model.api5.keywordbids.SetAutoRequestMap;
import ru.yandex.autotests.directapi.rules.ApiSteps;
import ru.yandex.autotests.directapi.rules.Trashman;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.annotations.TestCaseId;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.primitives.Ints.asList;
import static org.hamcrest.Matchers.comparesEqualTo;
import static org.hamcrest.Matchers.equalTo;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assertThat;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

@Aqua.Test
@Features(KeywordBidsFeatures.SET_AUTO)
@RunWith(Parameterized.class)
public class SetAutoFormulaForComputationSearchBidTest {
    private static final Logger log = LoggerFactory.getLogger(SetAutoFormulaForComputationSearchBidTest.class);

    private static final Long MAX_BID = MoneyCurrency.get(Currency.RUB).getLongMaxPrice().longValue();

    @ClassRule
    public static ApiSteps api = new ApiSteps();

    @ClassRule
    public static SemaphoreRule semaphore = Semaphore.getSemaphore();
    private static long keywordId;

    @Rule
    public Trashman trasher = new Trashman(api);

    private static final int INCREASE_PERCENT = 100;

    @Parameterized.Parameter
    public Integer targetTrafficVolume;

    @Parameterized.Parameters(name = "trafficVolume = {0}")
    public static Collection data() {
        // Эти ставки должны гарантированно отдаваться в KeywordBids.get
        return asList(5, 10, 15, 65, 75, 85, 90, 100).stream().map(i -> new Object[]{i}).collect(Collectors.toList());
    }

    @BeforeClass
    @Step("Подготовка данных для теста")
    public static void readBanner() {
        api.userSteps.clientFakeSteps().fakeClearClientSpentUnits(BidsLogins.BIDS_CLIENT);
        Long campaignId = api.userSteps.campaignSteps().addDefaultTextCampaignWithStrategies(
                new TextCampaignSearchStrategyAddMap().defaultHighestPosition(),
                new TextCampaignNetworkStrategyAddMap().defaultMaximumCoverage(),
                BidsLogins.BIDS_CLIENT
        );
        Long adGroupId = api.userSteps.adGroupsSteps().addDefaultGroup(campaignId, BidsLogins.BIDS_CLIENT);
        api.userSteps.adsSteps().addDefaultTextAd(adGroupId, BidsLogins.BIDS_CLIENT);
        keywordId = api.userSteps.keywordsSteps()
                .addKeyword(BidsLogins.BIDS_CLIENT, adGroupId, "арендовать квартиру");
        api.protocol(ProtocolType.SOAP);
    }

    @Test
    @TestCaseId("2967")
    public void checkPriceValueInKeywordBidsGet() {
        BigDecimal expectedPrice = getExpectedPrice(new PriceCalculator(targetTrafficVolume, INCREASE_PERCENT));
        api.userSteps.keywordBidsSteps().shouldGetKeywordBidResultOnSetAuto(
                new SetAutoRequestMap()
                        .withKeywordBids(
                                new KeywordBidsSetAutoItemMap()
                                        .withKeywordId(keywordId)
                                        .withBiddingRule(new SetAutoBiddingRule()
                                                .withSearchByTrafficVolume(new SearchByTrafficVolumeBiddingRule()
                                                        .withTargetTrafficVolume(targetTrafficVolume)
                                                        .withIncreasePercent(INCREASE_PERCENT)
                                                        .withBidCeiling(MAX_BID)))
                        ),
                BidsLogins.BIDS_CLIENT,
                KeywordBidExpectedResult.successWithKeywordId());
        GetResponse getResponse = api.userSteps.keywordBidsSteps().keywordBidsGet(
                new GetRequestMap()
                        .withSelectionCriteria(new KeywordBidsSelectionCriteriaMap()
                                .withKeywordIds(keywordId))
                        .withFieldNames(KeywordBidFieldEnum.KEYWORD_ID)
                        .withSearchFieldNames(KeywordBidSearchFieldEnum.BID),
                BidsLogins.BIDS_CLIENT
        );
        BigDecimal actualPrice = BigDecimal.valueOf(getResponse.getKeywordBids().get(0).getSearch().getBid(), 6);
        log.info("Ожидаемое значение " + expectedPrice);
        log.info("Полученное значение " + actualPrice);
        assertThat("значение поля Bid соответствует ожидаемому",
                actualPrice,
                comparesEqualTo(expectedPrice));
    }

    @Test
    @TestCaseId("2966")
    public void checkMaxPriceInKeywordBidsGet() {
        Long minPrice = MoneyCurrency.get(Currency.RUB).getLongMinPrice().longValue();
        api.userSteps.keywordBidsSteps().shouldGetKeywordBidResultOnSetAuto(
                new SetAutoRequestMap()
                        .withKeywordBids(
                                new KeywordBidsSetAutoItemMap()
                                        .withKeywordId(keywordId)
                                        .withBiddingRule(new SetAutoBiddingRule()
                                                .withSearchByTrafficVolume(new SearchByTrafficVolumeBiddingRule()
                                                        .withTargetTrafficVolume(targetTrafficVolume)
                                                        .withIncreasePercent(INCREASE_PERCENT)
                                                        .withBidCeiling(minPrice)))
                        ),
                BidsLogins.BIDS_CLIENT,
                KeywordBidExpectedResult.successWithKeywordId());

        KeywordBidGetItem bid = api.userSteps.keywordBidsSteps().keywordBidsGet(
                new GetRequestMap()
                        .withSelectionCriteria(new KeywordBidsSelectionCriteriaMap()
                                .withKeywordIds(keywordId))
                        .withFieldNames(KeywordBidFieldEnum.KEYWORD_ID)
                        .withSearchFieldNames(KeywordBidSearchFieldEnum.BID),
                BidsLogins.BIDS_CLIENT).getKeywordBids().get(0);
        assertThat("значение поля Bid соответствует ожидаемому", bid.getSearch().getBid(), equalTo(minPrice));
    }

    private static class PriceCalculator {
        private final Integer targetTrafficVolume;
        private final Integer increasePercent;

        private PriceCalculator(Integer targetTrafficVolume, Integer increasePercent) {
            this.targetTrafficVolume = targetTrafficVolume;
            this.increasePercent = increasePercent;
        }

        BigDecimal calculatePrice(KeywordBidGetItem bidGetItem) {
            AuctionBids auctionBids = bidGetItem.getSearch().getAuctionBids().getValue();
            Optional<AuctionBidItem> bidItem =
                    auctionBids.getAuctionBidItems().stream().filter(i -> i.getTrafficVolume() == targetTrafficVolume)
                            .findFirst();
            checkState(bidItem.isPresent());
            BigDecimal targetBid = BigDecimal.valueOf(bidItem.get().getBid(), 6);
            return targetBid.multiply(BigDecimal.ONE.add(BigDecimal.valueOf(increasePercent, 2)));
        }
    }

    private static BigDecimal getExpectedPrice(PriceCalculator priceCalculator) {
        GetResponse getResponse = api.userSteps.keywordBidsSteps().keywordBidsGet(
                new GetRequestMap()
                        .withSelectionCriteria(
                                new KeywordBidsSelectionCriteriaMap().withKeywordIds(keywordId)
                        )
                        .withFieldNames(KeywordBidFieldEnum.KEYWORD_ID)
                        .withSearchFieldNames(KeywordBidSearchFieldEnum.AUCTION_BIDS),
                BidsLogins.BIDS_CLIENT
        );
        assumeThat("получили верное кол-во ставок в get", getResponse.getKeywordBids().size(), equalTo(1));
        return priceCalculator.calculatePrice(getResponse.getKeywordBids().get(0));
    }

}
