package ru.yandex.autotests.directapi.steps.banners;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;

import com.yandex.direct.api.v5.adimages.AdImageActionResult;
import com.yandex.direct.api.v5.adimages.AdImageFieldEnum;
import com.yandex.direct.api.v5.adimages.AdImageGetItem;
import com.yandex.direct.api.v5.adimages.AddRequest;
import com.yandex.direct.api.v5.adimages.AddResponse;
import com.yandex.direct.api.v5.adimages.DeleteRequest;
import com.yandex.direct.api.v5.adimages.DeleteResponse;
import com.yandex.direct.api.v5.adimages.GetRequest;
import com.yandex.direct.api.v5.adimages.GetResponse;

import ru.yandex.autotests.directapi.apiclient.RequestHeader;
import ru.yandex.autotests.directapi.apiclient.config.ConnectionConfig;
import ru.yandex.autotests.directapi.apiclient.errors.Api5Error;
import ru.yandex.autotests.directapi.apiclient.version5.ServiceNames;
import ru.yandex.autotests.directapi.model.Image;
import ru.yandex.autotests.directapi.model.ValidImageData;
import ru.yandex.autotests.directapi.model.api5.Action;
import ru.yandex.autotests.directapi.model.api5.adimages.AdImageAddItemMap;
import ru.yandex.autotests.directapi.model.api5.adimages.AdImageExpectedResult;
import ru.yandex.autotests.directapi.model.api5.adimages.AdImageHashesCriteriaMap;
import ru.yandex.autotests.directapi.model.api5.adimages.AddRequestMap;
import ru.yandex.autotests.directapi.model.api5.adimages.DeleteRequestMap;
import ru.yandex.autotests.directapi.model.api5.adimages.GetRequestMap;
import ru.yandex.autotests.directapi.rules.Api5Bin;
import ru.yandex.autotests.directapi.rules.Api5Binable;
import ru.yandex.autotests.directapi.steps.BaseApiSteps;
import ru.yandex.autotests.irt.testutils.RandomUtils;
import ru.yandex.autotests.irt.testutils.allure.LogSteps;
import ru.yandex.qatools.allure.annotations.Step;

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

/**
 * Created by ginger on 04.02.16.
 */
public class ImagesStepsV5 extends BaseApiSteps implements Api5Binable<String> {
    private LogSteps log = LogSteps.getLogger(this.getClass());

    private static ImagesStepsV5 _instance;

    public Api5Bin<String> bin = new Api5Bin<>(this);

    private ImagesStepsV5(ConnectionConfig connectionConfig, RequestHeader requestHeader) {
        super(connectionConfig, requestHeader);
    }

    public static ImagesStepsV5 getInstance(ConnectionConfig connectionConfig, RequestHeader requestHeader) {
        if (_instance == null) {
            _instance = new ImagesStepsV5(connectionConfig, requestHeader);
        } else {
            _instance.setConnectionConfig(connectionConfig);
            _instance.setRequestHeader(requestHeader);
        }
        return _instance;

    }

    public void removeImagesFromQueue(List<String> hashes) {
        log.info("Удаляем картинки из очереди TRASHER'а");
        for (String hash : hashes) {
            bin.removeFromBin(hash);
        }
    }

    public void putImagesToQueue(String login, List<String> hashes) {
        log.info("Добавляем картинки в очередь на удаление " + hashes.toString());
        for (String hash : hashes) {
            bin.throwToBin(hash, login != null ? login :
                    (requestHeader.getFakeLogin() != null ? requestHeader.getFakeLogin() : requestHeader.getLogin()));
        }
    }


    @Override
    public Callable clearBin(final Map<String, String> binData) {
        return () -> {
            Map<String, List<String>> loginToHashes = new HashMap<>();
            for (Map.Entry<String, String> entry : binData.entrySet()) {
                String hash = entry.getKey();
                String login = entry.getValue();
                if (!loginToHashes.containsKey(login)) {
                    loginToHashes.put(login, new ArrayList<>());
                }
                loginToHashes.get(login).add(hash);
            }
            for (Map.Entry<String, List<String>> entry : loginToHashes.entrySet()) {
                String login = entry.getKey();
                List<String> hashes = entry.getValue();
                adImagesDelete(login, hashes);
            }
            return null;
        };
    }

    public List<String> addImagesForUserIfNotExists(String login, int imageAmount) {
        List<String> hashes = adImagesGetAllHashes(login);
        if (hashes.size() >= imageAmount) {
            return hashes;
        } else {
            deleteAllAdImagesByLogin(login);
            AdImageAddItemMap[] adImageAddItemMaps = new AdImageAddItemMap[imageAmount];
            for (int i = 0; i < imageAmount; i++) {
                Image image = ValidImageData.getValidRegularImage(i + 1);
                AdImageAddItemMap rawMap = new AdImageAddItemMap()
                        .withImageData(image.getUrl())
                        .withName(image.getName());
                adImageAddItemMaps[i] = rawMap;
            }
            hashes = adImagesAdd(login, adImageAddItemMaps);

            for (String hash : hashes) {
                assumeThat("картинка успешно добавилась", hash, notNullValue());
            }

            return hashes;
        }
    }

    //region Add

    @Step("[AdImages]: Add")
    public AddResponse adImagesAdd(String login, AddRequestMap parameters) {
        AddResponse response = defaultClientV5()
                .invokeMethod(ServiceNames.AD_IMAGES, login, Action.ADD, (AddRequest) parameters.getBean());
        List<String> hashes = extractHashesFromAdImageActionResult(response.getAddResults());
        putImagesToQueue(login, hashes);
        return response;
    }

    public AddResponse adImagesAdd(AddRequestMap parameters) {
        return adImagesAdd(null, parameters);
    }

    public List<String> adImagesAdd(String login, AdImageAddItemMap... adImages) {
        AddResponse response = adImagesAdd(login, new AddRequestMap().withAdImages(adImages));
        lookForResponseExceptions(response.getAddResults());
        return extractHashesFromAdImageActionResult(response.getAddResults());
    }

    public List<String> adImagesAdd(AdImageAddItemMap... adImages) {
        return adImagesAdd(null, adImages);
    }

    public void expectErrorOnAdd(String login, AddRequestMap parameters, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.AD_IMAGES, login, Action.ADD, parameters, api5Error);
    }

    public void expectErrorOnAdd(AddRequestMap parameters, Api5Error api5Error) {
        expectErrorOnAdd(null, parameters, api5Error);
    }

    public void expectErrorOnAdd(String login, Object parameters, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.AD_IMAGES, login, Action.ADD, parameters, api5Error);
    }

    public void expectErrorOnAdd(Object parameters, Api5Error api5Error) {
        expectErrorOnAdd(null, parameters, api5Error);
    }

    public void shouldGetResultOnAdd(String login, AddRequestMap parameters, AdImageExpectedResult... expectedResults) {
        AddResponse response = (AddResponse) shouldGetAdImageResultOn(
                login, ServiceNames.AD_IMAGES, Action.ADD, parameters, expectedResults);
        List<String> hashes = extractHashesFromAdImageActionResult(response.getAddResults());
        putImagesToQueue(login, hashes);
    }

    public void shouldGetResultOnAdd(AddRequestMap parameters, AdImageExpectedResult... expectedResults) {
        shouldGetResultOnAdd(null, parameters, expectedResults);
    }

    public String addDefaultRegularAdImage(String login) {
        Image image =
                ValidImageData.getValidRegularImage(RandomUtils.getRandomInteger(1, ValidImageData.regular.size()));
        return addImage(login, image);
    }

    public String addDefaultRegularAdImage() {
        return addDefaultRegularAdImage(null);
    }

    public String addDefaultWideAdImage(String login) {
        Image image = ValidImageData.getValidWideImage(RandomUtils.getRandomInteger(1, ValidImageData.wide.size()));
        return addImage(login, image);
    }

    public String addDefaultWideAdImage() {
        return addDefaultWideAdImage(null);
    }

    public String addImage(String login, Image image) {
        return adImagesAdd(
                login,
                new AdImageAddItemMap()
                        .withImageData(image.getUrl())
                        .withName(image.getName())
        ).get(0);
    }

    public String addImage(Image image) {
        return addImage(null, image);
    }

    public String addImageAdAdImage(String login, Integer imageNumber) {
        Image image = ValidImageData.getValidImageAdImage(imageNumber);
        return addImage(login, image);
    }

    public String addImageAdAdImage(Integer imageNumber) {
        return addImageAdAdImage(null, imageNumber);
    }

    public String addDefaultImageAdAdImage() {
        return addDefaultImageAdAdImage(null);
    }

    public String addDefaultImageAdAdImage(String login) {
        return addImageAdAdImage(login, null);
    }

    //endregion


    //region Get
    @Step("[AdImages]: Get")
    public GetResponse adImagesGet(GetRequestMap parameters, String login) {
        return defaultClientV5()
                .invokeMethod(ServiceNames.AD_IMAGES, login, Action.GET, (GetRequest) parameters.getBean());
    }

    public GetResponse adImagesGet(GetRequestMap parameters) {
        return adImagesGet(parameters, null);
    }

    public List<String> adImagesGetAllHashes(String login) {
        GetResponse getResponse =
                adImagesGet(new GetRequestMap().withFieldNames(AdImageFieldEnum.AD_IMAGE_HASH), login);
        return extractHashesFromGetResponse(getResponse);
    }

    public List<String> adImagesGetAllHashes() {
        return adImagesGetAllHashes(null);
    }

    public void expectErrorOnAdImagesGet(GetRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.AD_IMAGES, login, Action.GET, parameters, api5Error);
    }

    public void expectErrorOnAdImagesGet(GetRequestMap parameters, Api5Error api5Error) {
        expectErrorOnAdImagesGet(parameters, null, api5Error);
    }

    public void expectErrorOnAdImagesGet(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.AD_IMAGES, login, Action.GET, parameters, api5Error);
    }

    public void expectErrorOnAdImagesGet(Object parameters, Api5Error api5Error) {
        expectErrorOnAdImagesGet(parameters, null, api5Error);
    }
    //endregion


    //region Delete
    @Step("[AdImages]: Delete")
    public DeleteResponse adImagesDelete(DeleteRequestMap parameters, String login) {
        DeleteResponse response = defaultClientV5()
                .invokeMethod(ServiceNames.AD_IMAGES, login, Action.DELETE, (DeleteRequest) parameters.getBean());
        List<String> hashes = extractHashesFromAdImageActionResult(response.getDeleteResults());
        removeImagesFromQueue(hashes);
        return response;
    }

    public DeleteResponse adImagesDelete(DeleteRequestMap parameters) {
        return adImagesDelete(parameters, null);
    }


    public DeleteResponse adImagesDelete(String login, List<String> hashes) {
        return adImagesDelete(
                new DeleteRequestMap()
                        .withSelectionCriteria(
                                new AdImageHashesCriteriaMap()
                                        .withAdImageHashes(hashes.stream().toArray(String[]::new))),
                login
        );
    }

    public DeleteResponse adImagesDelete(String... hashes) {
        return adImagesDelete(new DeleteRequestMap()
                .withSelectionCriteria(new AdImageHashesCriteriaMap().withAdImageHashes(hashes)));
    }

    public void deleteAllAdImagesByLogin(String login) {
        List<String> hashes = adImagesGetAllHashes(login);
        if (hashes.size() > 0) {
            String[] hashesArray = hashes.stream().toArray(String[]::new);
            adImagesDelete(hashesArray);
        } else {
            log.info("У пользователя нету картинок.");
        }
    }

    public void shouldGetResultOnDelete(
            DeleteRequestMap parameters, String login, AdImageExpectedResult... expectedResults)
    {
        DeleteResponse response =
                (DeleteResponse) shouldGetAdImageResultOn(login, ServiceNames.AD_IMAGES, Action.DELETE, parameters,
                        expectedResults);
        List<String> hashes = extractHashesFromAdImageActionResult(response.getDeleteResults());
        removeImagesFromQueue(hashes);
    }

    public void shouldGetResultOnDelete(DeleteRequestMap parameters, AdImageExpectedResult... expectedResults) {
        shouldGetResultOnDelete(parameters, null, expectedResults);
    }

    public void shouldGetResultOnDelete(
            AdImageHashesCriteriaMap parameters, String login, AdImageExpectedResult... expectedResults)
    {
        shouldGetResultOnDelete(new DeleteRequestMap().withSelectionCriteria(parameters), login, expectedResults);
    }

    public void shouldGetResultOnDelete(AdImageHashesCriteriaMap parameters, AdImageExpectedResult... expectedResults) {
        shouldGetResultOnDelete(parameters, null, expectedResults);
    }

    public void expectErrorOnAdImagesDelete(DeleteRequestMap parameters, String login, Api5Error api5Error) {
        shouldGetErrorOn(ServiceNames.AD_IMAGES, login, Action.DELETE, parameters, api5Error);
    }

    public void expectErrorOnAdImagesDelete(DeleteRequestMap parameters, Api5Error api5Error) {
        expectErrorOnAdImagesDelete(parameters, null, api5Error);
    }

    public void expectErrorOnAdImagesDelete(Object parameters, String login, Api5Error api5Error) {
        shouldGetJSONErrorOn(ServiceNames.AD_IMAGES, login, Action.DELETE, parameters, api5Error);
    }

    public void expectErrorOnAdImagesDelete(Object parameters, Api5Error api5Error) {
        expectErrorOnAdImagesDelete(parameters, null, api5Error);
    }
    //endregion

    private void lookForResponseExceptions(List<AdImageActionResult> results) {
        Optional<AdImageActionResult> notSuccessfullResult =
                results.stream()
                        .filter(result -> (result.getErrors().size() != 0) || (result.getWarnings().size() != 0))
                        .findFirst();
        assertThat("отсутствуют ошибки и ворнинги при вызове метода", notSuccessfullResult.isPresent(), equalTo(false));
    }

    private List<String> extractHashesFromAdImageActionResult(List<AdImageActionResult> results) {
        return results.stream()
                .map(AdImageActionResult::getAdImageHash)
                .filter(adImageHash -> adImageHash != null)
                .collect(Collectors.toList());
    }

    private List<String> extractHashesFromGetResponse(GetResponse getResponse) {
        return getResponse.getAdImages().stream()
                .map(AdImageGetItem::getAdImageHash)
                .filter(adImageHash -> adImageHash != null)
                .collect(Collectors.toList());
    }

}
