package ru.yandex.autotests.directapi.campaigns.geteventslog.nds;

import java.math.RoundingMode;

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.utils.money.Currency;
import ru.yandex.autotests.direct.utils.money.Money;
import ru.yandex.autotests.directapi.apiclient.config.Semaphore;
import ru.yandex.autotests.directapi.campaigns.CampaignFeatures;
import ru.yandex.autotests.directapi.common.api45.Account;
import ru.yandex.autotests.directapi.common.api45.EventsLogItem;
import ru.yandex.autotests.directapi.model.Logins;
import ru.yandex.autotests.directapi.model.User;
import ru.yandex.autotests.directapi.model.finances.PaymentMap;
import ru.yandex.autotests.directapi.model.geteventslog.EventType;
import ru.yandex.autotests.directapi.model.geteventslog.GetEventsLogFilterMap;
import ru.yandex.autotests.directapi.model.geteventslog.GetEventsLogRequestMap;
import ru.yandex.autotests.directapi.rules.ApiSteps;
import ru.yandex.autotests.directapi.rules.Trashman;
import ru.yandex.autotests.directapi.steps.ConditionFactories;
import ru.yandex.autotests.irt.testutils.allure.LogSteps;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.hazelcast.SemaphoreRule;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static ru.yandex.autotests.irt.testutils.allure.TestSteps.assumeThat;

@Aqua.Test
@Features(CampaignFeatures.GET_EVENTS_LOG)
public class GetEventsLogAgencyNdsTest {

    private LogSteps log = LogSteps.getLogger(this.getClass());

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

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

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

    private static final String AGENCY = Logins.AGENCY_SHARD_2;
    private static final String SUBCLIENT_1 = Logins.ACCOUNT_SUBCLIENT_SHARD;
    private static final String SUBCLIENT_2 = Logins.ACCOUNT_SUBCLIENT_SHARD_2;

    private Account account1;
    private Account account2;
    private Long clientId1;
    private Long clientId2;

    @BeforeClass
    public static void init() {
        api.userSteps.clientFakeSteps().enableAgencyWallet(AGENCY);
        api.as(Logins.AGENCY_SHARD_2).userSteps.createAccount(SUBCLIENT_1);
        api.as(Logins.AGENCY_SHARD_2).userSteps.createAccount(SUBCLIENT_2);
    }

    @Before
    public void before() {
        String accountOwner = Logins.AGENCY_SHARD_2;

        account1 = api.as(accountOwner).userSteps.financeSteps().getAccount(SUBCLIENT_1);
        api.userSteps.financeSteps().verifyCampaignExistsInBalance(account1.getAccountID());
        api.userSteps.balanceSteps().synchronizeWithBalance(account1.getAccountID());

        account2 = api.as(accountOwner).userSteps.financeSteps().getAccount(SUBCLIENT_2);
        api.userSteps.financeSteps().verifyCampaignExistsInBalance(account2.getAccountID());
        api.userSteps.balanceSteps().synchronizeWithBalance(account2.getAccountID());

        clientId1 = Long.valueOf(User.get(SUBCLIENT_1).getClientID());
        clientId2 = Long.valueOf(User.get(SUBCLIENT_2).getClientID());

        api.userSteps.getDirectJooqDbSteps().useShardForLogin(SUBCLIENT_1).eventLogSteps().deleteAllEvents(clientId1);
        api.userSteps.getDirectJooqDbSteps().useShardForLogin(SUBCLIENT_2).eventLogSteps().deleteAllEvents(clientId2);
    }

    @Test
    public void getEventsLog_PaymentFrom2003_AmountMustBeCountedWithNds20() {
        api.as(AGENCY).userSteps.financeSteps().deposit(
                new PaymentMap(api.type())
                        .withAccountID(account1.getAccountID())
                        .withCurrency(Currency.RUB)
                        .withAmount(1200.0f * 2)
                        .withContract(api.userSteps.balanceSteps().getDefaultContract(User.get(AGENCY).getClientID()))
        );

        ConditionFactories.NOTIFY_ORDER2.until(
                api.userSteps.financeSteps().accountAmountChanged(account1), equalTo(true));

        api.userSteps.getDirectJooqDbSteps().useShardForLogin(SUBCLIENT_1).eventLogSteps().moveAllEventsInTime(clientId1,
                "2003-06-15 00:00:00");

        EventsLogItem[] eventsLog = api.userSteps.getEventsLogSteps().getEventsLog(
                new GetEventsLogRequestMap(api.type())
                        .withLogins(SUBCLIENT_1)
                        .withCurrency(Currency.RUB)
                        .withFilter(new GetEventsLogFilterMap(api.type())
                                .withEventType(EventType.MONEYIN)
                                .withAccountIDS(account1.getAccountID()))
                        .withTimestampFrom("2003-06-14T00:00:00Z")
                        .withTimestampTo("2003-06-16T00:00:00Z"));

        assumeThat("Вернулось одно событие", eventsLog.length, is(1));
        EventsLogItem logItem = eventsLog[0];

        assumeThat("У события правильно проставлена дата", logItem.getTimestamp(), is("2003-06-14T20:00:00Z"));

        assertThat("Счет пополнился на сумму с НДС 20%", logItem.getAttributes().getPayed(), is(1000.0f * 2));
    }

    @Test
    public void getEventsLog_PaymentFrom2004_AmountMustBeCountedWithNds18() {
        api.as(AGENCY).userSteps.financeSteps().deposit(
                new PaymentMap(api.type())
                        .withAccountID(account2.getAccountID())
                        .withCurrency(Currency.RUB)
                        .withAmount(1180.0f * 2)
                        .withContract(api.userSteps.balanceSteps().getDefaultContract(User.get(AGENCY).getClientID()))
        );

        ConditionFactories.NOTIFY_ORDER2.until(
                api.userSteps.financeSteps().accountAmountChanged(account2), equalTo(true));

        api.userSteps.getDirectJooqDbSteps().useShardForLogin(SUBCLIENT_2).eventLogSteps().moveAllEventsInTime(clientId2,
                "2004-06-15 00:00:00");

        EventsLogItem[] eventsLog = api.userSteps.getEventsLogSteps().getEventsLog(
                new GetEventsLogRequestMap(api.type())
                        .withLogins(SUBCLIENT_2)
                        .withCurrency(Currency.RUB)
                        .withFilter(new GetEventsLogFilterMap(api.type())
                                .withEventType(EventType.MONEYIN)
                                .withAccountIDS(account2.getAccountID()))
                        .withTimestampFrom("2004-06-14T00:00:00Z")
                        .withTimestampTo("2004-06-16T00:00:00Z"));

        assumeThat("Вернулось одно событие", eventsLog.length, is(1));
        EventsLogItem logItem = eventsLog[0];

        assumeThat("У события правильно проставлена дата", logItem.getTimestamp(), is("2004-06-14T20:00:00Z"));

        assertThat("Счет пополнился на сумму с НДС 18%", logItem.getAttributes().getPayed(), is(1000.0f * 2));
    }

    @Test
    public void getEventsLog_PaymentsFrom2004AndNow_DifferentSubclients_AmountsMustBeCountedWithDifferentNds() {
        api.as(AGENCY).userSteps.financeSteps().deposit(
                new PaymentMap(api.type())
                        .withAccountID(account1.getAccountID())
                        .withCurrency(Currency.RUB)
                        .withAmount(1180.0f * 2)
                        .withContract(api.userSteps.balanceSteps().getDefaultContract(User.get(AGENCY).getClientID()))
        );

        ConditionFactories.NOTIFY_ORDER2.until(
                api.userSteps.financeSteps().accountAmountChanged(account1), equalTo(true));

        api.userSteps.getDirectJooqDbSteps().useShardForLogin(SUBCLIENT_1).eventLogSteps().moveAllEventsInTime(clientId1,
                "2004-06-15 00:00:00");

        api.as(AGENCY).userSteps.financeSteps().deposit(
                new PaymentMap(api.type())
                        .withAccountID(account2.getAccountID())
                        .withCurrency(Currency.RUB)
                        .withAmount(1200.0f * 2)
                        .withContract(api.userSteps.balanceSteps().getDefaultContract(User.get(AGENCY).getClientID()))
        );

        ConditionFactories.NOTIFY_ORDER2.until(
                api.userSteps.financeSteps().accountAmountChanged(account2), equalTo(true));

        EventsLogItem[] eventsLog = api.userSteps.getEventsLogSteps().getEventsLog(
                new GetEventsLogRequestMap(api.type())
                        .withLogins(SUBCLIENT_1, SUBCLIENT_2)
                        .withCurrency(Currency.RUB)
                        .withFilter(new GetEventsLogFilterMap(api.type())
                                .withEventType(EventType.MONEYIN)
                                .withAccountIDS(account1.getAccountID(), account2.getAccountID()))
                        .withTimestampFrom("2004-06-14T00:00:00Z"));

        assumeThat("Вернулось два события", eventsLog.length, is(2));

        sortEvents(eventsLog, "2004-06-14T20:00:00Z");

        assumeThat("У события правильно проставлена дата", eventsLog[0].getTimestamp(), is("2004-06-14T20:00:00Z"));

        assertThat("Счет пополнился на сумму с НДС 18%",
                eventsLog[0].getAttributes().getPayed(), is(1000.0f * 2));
        assertThat("Счет пополнился на сумму с НДС 20%",
                eventsLog[1].getAttributes().getPayed(),
                is(Money.valueOf(1200.0f * 2, Currency.RUB).subtractVAT().setScale(2, RoundingMode.UP).floatValue()));
    }

    @Test
    public void getEventsLog_PaymentsFrom2004AndNow_SameSubclient_AmountsMustBeCountedWithDifferentNds() {
        api.as(AGENCY).userSteps.financeSteps().deposit(
                new PaymentMap(api.type())
                        .withAccountID(account1.getAccountID())
                        .withCurrency(Currency.RUB)
                        .withAmount(1180.0f * 2)
                        .withContract(api.userSteps.balanceSteps().getDefaultContract(User.get(AGENCY).getClientID()))
        );

        ConditionFactories.NOTIFY_ORDER2.until(
                api.userSteps.financeSteps().accountAmountChanged(account1), equalTo(true));

        api.userSteps.getDirectJooqDbSteps().useShardForLogin(SUBCLIENT_1).eventLogSteps().moveAllEventsInTime(clientId1,
                "2004-06-15 00:00:00");

        api.as(AGENCY).userSteps.financeSteps().deposit(
                new PaymentMap(api.type())
                        .withAccountID(account1.getAccountID())
                        .withCurrency(Currency.RUB)
                        .withAmount(1200.0f * 2)
                        .withContract(api.userSteps.balanceSteps().getDefaultContract(User.get(AGENCY).getClientID()))
        );

        ConditionFactories.NOTIFY_ORDER2.until(
                api.userSteps.financeSteps().accountAmountChanged(account1), equalTo(true));

        EventsLogItem[] eventsLog = api.userSteps.getEventsLogSteps().getEventsLog(
                new GetEventsLogRequestMap(api.type())
                        .withLogins(SUBCLIENT_1, SUBCLIENT_2)
                        .withCurrency(Currency.RUB)
                        .withFilter(new GetEventsLogFilterMap(api.type())
                                .withEventType(EventType.MONEYIN)
                                .withAccountIDS(account1.getAccountID(), account2.getAccountID()))
                        .withTimestampFrom("2004-06-14T00:00:00Z"));

        assumeThat("Вернулось два события", eventsLog.length, is(2));

        sortEvents(eventsLog, "2004-06-14T20:00:00Z");

        assumeThat("У события правильно проставлена дата", eventsLog[0].getTimestamp(), is("2004-06-14T20:00:00Z"));

        assertThat("Счет пополнился на сумму с НДС 18%",
                eventsLog[0].getAttributes().getPayed(), is(1000.0f * 2));
        assertThat("Счет пополнился на сумму с текущим НДС",
                eventsLog[1].getAttributes().getPayed(),
                is(Money.valueOf(1200.0f * 2, Currency.RUB).subtractVAT().setScale(2, RoundingMode.UP).floatValue()));
    }

    private void sortEvents(EventsLogItem[] eventsLog, String firstTimestamp) {
        if (!eventsLog[0].getTimestamp().equals(firstTimestamp)) {
            EventsLogItem tmp = eventsLog[1];
            eventsLog[1] = eventsLog[0];
            eventsLog[0] = tmp;
        }
    }
}
