package ru.yandex.autotests.direct.api.adgroups.add;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.TreeMap;

import javax.xml.ws.handler.MessageContext;

import com.sun.xml.ws.developer.WSBindingProvider;
import com.yandex.direct.api.v5.adgroups.AdGroupsPort;
import com.yandex.direct.api.v5.adgroups.AddRequest;
import com.yandex.direct.api.v5.adgroups.ApiException;
import org.apache.commons.lang3.StringUtils;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;

import ru.yandex.aqua.annotations.project.Aqua;
import ru.yandex.autotests.direct.api.adgroups.AdGroupsFeatures;
import ru.yandex.autotests.direct.api.adgroups.AdGroupsLogins;
import ru.yandex.autotests.directapi.ApiStories;
import ru.yandex.autotests.directapi.apiclient.config.Semaphore;
import ru.yandex.autotests.directapi.apiclient.errors.Api5Error;
import ru.yandex.autotests.directapi.apiclient.errors.Api5ErrorDetails;
import ru.yandex.autotests.directapi.apiclient.errors.Api5ErrorDetailsJava;
import ru.yandex.autotests.directapi.model.api5.adgroups.AdGroupAddItemMap;
import ru.yandex.autotests.directapi.model.api5.adgroups.AddRequestMap;
import ru.yandex.autotests.directapi.model.api5.general.ExpectedResult;
import ru.yandex.autotests.directapi.model.api5.general.Notification;
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.Stories;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

import static org.apache.commons.lang3.StringUtils.capitalize;
import static org.hamcrest.Matchers.equalTo;
import static ru.yandex.autotests.directapi.model.AuthenticationData.APP_ACCEPTED_FULL_REQUEST;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assertThat;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

/**
 * Created by pavryabov on 11.12.14.
 * https://st.yandex-team.ru/TESTIRT-3706
 */
@Aqua.Test
@Features(AdGroupsFeatures.ADD)
@Stories(ApiStories.UNITS)
public class AdGroupsAddUnitsTest {
    //https://st.yandex-team.ru/DIRECT-37648

    private static final String client = AdGroupsLogins.ADD_UNITS;
    private static final String clientRep = AdGroupsLogins.ADD_UNITS_REP;
    private static final String clientWithCoef = AdGroupsLogins.CLIENT_FOR_CHECK_APPS;

    private static final String OLD_APP = "be783aeda834439790de5be3bdce4f9c";

    private static final Double COEF = 0.8;

    @ClassRule
    public static final ApiSteps api = new ApiSteps().version(104).as(client);

    @ClassRule
    public static final SemaphoreRule semaphore = Semaphore.getSemaphore();

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

    static Long campaignId;

    private static final int ADD_COST = 20;
    private static final int ADD_COST_GROUP = 20;
    private static final int DEFAULT_ERROR_COST = 20;
    private static final int COMMON_REQUEST_ERROR = 50;
    private static final String UNITS = "Units";

    @BeforeClass
    public static void initTest() {
        api.userSteps.clientFakeSteps().fakeClearClientSpentUnits(client);
        campaignId = api.userSteps.campaignSteps().addDefaultTextCampaign(client);
    }

    @Before
    public void clearSpentUnits() {
        api.userSteps.clientFakeSteps().fakeClearClientSpentUnits(client);
        api.as(client).token(OLD_APP);
    }

    @Test
    public void addOneGroup() {
        int unitsBefore = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        api.userSteps.adGroupsSteps().adGroupsAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap().defaultAdGroupAddItem(campaignId)
                )
        );
        int unitsAfter = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        assertThat("списалось правильное количество баллов",
                unitsBefore - unitsAfter, equalTo(ADD_COST + ADD_COST_GROUP));
    }

    @Test
    public void addTwoGroups() {
        int unitsBefore = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        api.userSteps.adGroupsSteps().adGroupsAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap().defaultAdGroupAddItem(campaignId),
                        new AdGroupAddItemMap().defaultAdGroupAddItem(campaignId)
                )
        );
        int unitsAfter = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        assertThat("списалось правильное количество баллов",
                unitsBefore - unitsAfter, equalTo(ADD_COST + 2 * ADD_COST_GROUP));
    }

    @Test
    public void callAdGroupsWithZeroUnits() {
        int units = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        api.userSteps.clientFakeSteps().fakeWithdrawClientUnits(client, units);
        units = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        assumeThat("у клиента не осталось баллов", units, equalTo(0));
        api.userSteps.adGroupsSteps().expectErrorOnAdGroupsAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap().defaultAdGroupAddItem(campaignId)
                ),
                new Api5Error(152, Api5ErrorDetails.NOT_ENOUGH_UNITS_FOR_OPERATION)
        );
    }

    @Test
    public void addOneGroupWithError() {
        //DIRECT-37674
        int unitsBefore = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        api.userSteps.adGroupsSteps().shouldGetResultOnAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap().defaultAdGroupAddItem(-campaignId)
                ),
                ExpectedResult.errors(new Notification(5005,
                        Api5ErrorDetailsJava.FIELD_MUST_BE_POSITIVE_INTEGER,
                        capitalize(AdGroupAddItemMap.CAMPAIGN_ID))));
        int unitsAfter = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        assertThat("списалось правильное количество баллов",
                unitsBefore - unitsAfter, equalTo(ADD_COST + DEFAULT_ERROR_COST));
    }

    @Test
    public void addTwoGroupsWithOneError() {
        //DIRECT-37674
        int unitsBefore = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        api.userSteps.adGroupsSteps().shouldGetResultOnAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap().defaultAdGroupAddItem(-campaignId),
                        new AdGroupAddItemMap().defaultAdGroupAddItem(campaignId)
                ),
                ExpectedResult.errors(new Notification(5005,
                        Api5ErrorDetailsJava.FIELD_MUST_BE_POSITIVE_INTEGER,
                        capitalize(AdGroupAddItemMap.CAMPAIGN_ID))),
                ExpectedResult.success());

        int unitsAfter = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        assertThat("списалось правильное количество баллов",
                unitsBefore - unitsAfter, equalTo(ADD_COST + DEFAULT_ERROR_COST + ADD_COST_GROUP));
    }

    @Test
    public void addTwoGroupsWithTwoErrors() {
        //DIRECT-37674
        int unitsBefore = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        api.userSteps.adGroupsSteps().shouldGetResultOnAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap().defaultAdGroupAddItem(-campaignId),
                        new AdGroupAddItemMap().defaultAdGroupAddItem(-campaignId)
                ),
                ExpectedResult.errors(new Notification(5005,
                        Api5ErrorDetailsJava.FIELD_MUST_BE_POSITIVE_INTEGER,
                        StringUtils.capitalize(AdGroupAddItemMap.CAMPAIGN_ID))),
                ExpectedResult.errors(new Notification(5005,
                        Api5ErrorDetailsJava.FIELD_MUST_BE_POSITIVE_INTEGER,
                        StringUtils.capitalize(AdGroupAddItemMap.CAMPAIGN_ID))));
        int unitsAfter = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        assertThat("списалось правильное количество баллов",
                unitsBefore - unitsAfter, equalTo(ADD_COST + 2 * DEFAULT_ERROR_COST));
    }

    @Test
    public void checkUnitsInHeader() throws ApiException {
        AdGroupsPort adGroupsPort = api.userSteps.soapClientV5().adGroupsService(null);
        adGroupsPort.add((AddRequest) new AddRequestMap().withAdGroups(
                new AdGroupAddItemMap().defaultAdGroupAddItem(campaignId)).getBean());
        WSBindingProvider bp = (WSBindingProvider) adGroupsPort;
        TreeMap responseHeaders = (TreeMap) bp.getResponseContext().get(MessageContext.HTTP_RESPONSE_HEADERS);
        assumeThat("в HTTP заголовке ответа есть поле Units", responseHeaders.containsKey(UNITS), equalTo(true));
        assertThat("Units выводится в правильном формате",
                ((String) ((List) responseHeaders.get(UNITS)).get(0)).matches("^\\d+/\\d+/\\d+$"), equalTo(true));
    }

    @Test
    public void addGroupFromRep() {
        int unitsBefore = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        api.as(clientRep).userSteps.adGroupsSteps().adGroupsAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap().defaultAdGroupAddItem(campaignId)
                )
        );
        int unitsAfter = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        assertThat("списалось правильное количество баллов",
                unitsBefore - unitsAfter, equalTo(ADD_COST + ADD_COST_GROUP));
    }

    @Test
    public void addWithInvalidRequest() {
        //DIRECT-37674
        int unitsBefore = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        api.userSteps.adGroupsSteps().expectErrorOnAdGroupsAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap()
                                .withCampaignId(campaignId)
                                .withRegionIds(0L),
                        new AdGroupAddItemMap()
                                .withCampaignId(campaignId)
                                .withRegionIds(0L)
                                .withName(AdGroupAddItemMap.DEFAULT_NAME)
                ),
                new Api5Error(8000, Api5ErrorDetailsJava.IN_ELEMENT_OF_ARRAY_MISSING_REQUIRED_FIELD,
                        capitalize(AddRequestMap.AD_GROUPS),
                        capitalize(AdGroupAddItemMap.NAME)));
        int unitsAfter = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(client);
        assertThat("списалось правильное количество баллов",
                unitsBefore - unitsAfter, equalTo(COMMON_REQUEST_ERROR));
    }

    @Test
    public void addOneGroupWithCoef() {
        int unitsBefore = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(clientWithCoef);
        api.as(clientWithCoef).userSteps.adGroupsSteps().adGroupsAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap().defaultAdGroupAddItem(campaignId)
                )
        );
        int unitsAfter = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(clientWithCoef);
        assertThat("списалось правильное количество баллов",
                unitsBefore - unitsAfter,
                equalTo(BigDecimal.valueOf(ADD_COST + DEFAULT_ERROR_COST)
                        .multiply(BigDecimal.valueOf(COEF)).setScale(0, RoundingMode.UP).intValue()));
    }

    @Test
    public void addWithInvalidRequestWithCoef() {
        int unitsBefore = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(clientWithCoef);
        api.as(clientWithCoef).userSteps.adGroupsSteps().expectErrorOnAdGroupsAdd(
                new AddRequestMap().withAdGroups(
                        new AdGroupAddItemMap()
                                .withCampaignId(campaignId)
                                .withRegionIds(0L),
                        new AdGroupAddItemMap()
                                .withCampaignId(campaignId)
                                .withRegionIds(0L)
                                .withName(AdGroupAddItemMap.DEFAULT_NAME)
                ),
                new Api5Error(8000, Api5ErrorDetailsJava.IN_ELEMENT_OF_ARRAY_MISSING_REQUIRED_FIELD,
                        capitalize(AddRequestMap.AD_GROUPS),
                        capitalize(AdGroupAddItemMap.NAME)));

        int unitsAfter = api.userSteps.clientFakeSteps().fakeClientUnitsBalance(clientWithCoef);
        assertThat("списалось правильное количество баллов",
                unitsBefore - unitsAfter,
                equalTo(BigDecimal.valueOf(COMMON_REQUEST_ERROR)
                        .multiply(BigDecimal.valueOf(COEF)).setScale(0, RoundingMode.UP).intValue()));
    }
}
