package ru.yandex.qe.dispenser.ws.quota.request.workflow.context;

import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.yandex.qe.dispenser.api.v1.request.quota.BodyUpdate;
import ru.yandex.qe.dispenser.domain.BotCampaignGroup;
import ru.yandex.qe.dispenser.domain.Person;
import ru.yandex.qe.dispenser.domain.Project;
import ru.yandex.qe.dispenser.domain.QuotaChangeRequest;
import ru.yandex.qe.dispenser.domain.dao.goal.Goal;
import ru.yandex.qe.dispenser.domain.hierarchy.Hierarchy;

import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@ParametersAreNonnullByDefault
public class UpdateRequestContext extends RequestContext {

    @NotNull
    private final List<? extends QuotaChangeRequest.ChangeAmount> changes;
    @NotNull
    private final QuotaChangeRequest updatedRequest;
    @NotNull
    protected final Set<QuotaChangeRequest.Field> changedFields = EnumSet.noneOf(QuotaChangeRequest.Field.class);

    protected final boolean onlyDecrease;

    public UpdateRequestContext(final Person person, final QuotaChangeRequest quotaChangeRequest,
                                final List<? extends QuotaChangeRequest.ChangeAmount> changes,
                                final BodyUpdate bodyUpdate) {
        this(person, quotaChangeRequest, changes, bodyUpdate, null, null, null);
    }


    public UpdateRequestContext(final Person person, final QuotaChangeRequest quotaChangeRequest,
                                final List<? extends QuotaChangeRequest.ChangeAmount> changes,
                                final BodyUpdate bodyUpdate,
                                @Nullable final Goal goal,
                                @Nullable final QuotaChangeRequest.Campaign campaign,
                                @Nullable final BotCampaignGroup botCampaignGroup) {
        super(person, quotaChangeRequest, goal, campaign, botCampaignGroup);

        final QuotaChangeRequest.Builder updatedRequestBuilder = quotaChangeRequest.copyBuilder();

        if (!changes.isEmpty()) {
            final List<QuotaChangeRequest.Change> oldChanges = quotaChangeRequest.getChanges();
            final Map<QuotaChangeRequest.ChangeKey, Long> amountByChangeOld = new HashMap<>();
            for (final QuotaChangeRequest.Change oldChange : oldChanges) {
                amountByChangeOld.put(oldChange.getKey(), oldChange.getAmount());
            }
            final Set<QuotaChangeRequest.ChangeKey> newChangeKeys = changes.stream()
                    .map(QuotaChangeRequest.ChangeAmount::getKey).collect(Collectors.toSet());
            final List<Pair<Long, Long>> diff = changes.stream()
                    .filter(c -> amountByChangeOld.containsKey(c.getKey()))
                    .map(c -> Pair.of(amountByChangeOld.get(c.getKey()), c.getAmount()))
                    .collect(Collectors.toList());

            boolean hasModifiedChanges = changes.stream().anyMatch(c -> !amountByChangeOld.containsKey(c.getKey())) || diff.stream().anyMatch(p -> !p.getLeft().equals(p.getRight()));
            this.onlyDecrease = diff.size() == changes.size() && diff.stream().allMatch(c -> c.getLeft() >= c.getRight());

            if (oldChanges.stream().anyMatch(c -> !newChangeKeys.contains(c.getKey()))) {
                hasModifiedChanges = true;
            }
            if (hasModifiedChanges) {
                changedFields.add(QuotaChangeRequest.Field.CHANGES);
            }
            this.changes = hasModifiedChanges ? changes : Collections.emptyList();
        } else {
            this.onlyDecrease = false;
            this.changes = Collections.emptyList();
        }

        prepareFields(quotaChangeRequest, bodyUpdate, updatedRequestBuilder);

        updatedRequest = updatedRequestBuilder.build();
    }

    protected void prepareFields(final QuotaChangeRequest quotaChangeRequest, final BodyUpdate bodyUpdate, final QuotaChangeRequest.Builder updatedRequestBuilder) {
        final String description = bodyUpdate.getDescription();
        if (isFieldChanged(description, quotaChangeRequest.getDescription())) {
            updatedRequestBuilder.description(description);
            changedFields.add(QuotaChangeRequest.Field.DESCRIPTION);
        }

        final String comment = bodyUpdate.getComment();
        if (isFieldChanged(comment, quotaChangeRequest.getComment())) {
            updatedRequestBuilder.comment(comment);
            changedFields.add(QuotaChangeRequest.Field.COMMENT);
        }

        final String calculations = bodyUpdate.getCalculations();
        if (isFieldChanged(calculations, quotaChangeRequest.getCalculations())) {
            updatedRequestBuilder.calculations(calculations);
            changedFields.add(QuotaChangeRequest.Field.CALCULATION);
        }

        final List<String> links = bodyUpdate.getChartLinks();
        if (isFieldChanged(links, quotaChangeRequest.getChartLinks())) {
            updatedRequestBuilder.chartLinks(links);
            changedFields.add(QuotaChangeRequest.Field.CHART_LINKS);
        }

        final String chartLinksAbsenceExplanation = bodyUpdate.getChartLinksAbsenceExplanation();
        if (isFieldChanged(chartLinksAbsenceExplanation, quotaChangeRequest.getChartLinksAbsenceExplanation())) {
            updatedRequestBuilder.chartLinksAbsenceExplanation(chartLinksAbsenceExplanation);
            changedFields.add(QuotaChangeRequest.Field.CHART_LINKS_ABSENCE_EXPLANATION);
        }

        final Map<String, String> additionalProperties = bodyUpdate.getAdditionalProperties();
        if (isFieldChanged(additionalProperties, quotaChangeRequest.getAdditionalProperties())) {
            updatedRequestBuilder.additionalProperties(additionalProperties);
            changedFields.add(QuotaChangeRequest.Field.ADDITIONAL_PROPERTIES);
        }

        final String summary = bodyUpdate.getSummary();
        if (isFieldChanged(summary, quotaChangeRequest.getSummary())) {
            updatedRequestBuilder.summary(summary);
            changedFields.add(QuotaChangeRequest.Field.SUMMARY);
        }

        final String projectKey = bodyUpdate.getProjectKey();
        if (isFieldChanged(projectKey, quotaChangeRequest.getProject().getPublicKey())) {
            final Project project = Hierarchy.get().getProjectReader().read(projectKey);
            updatedRequestBuilder.project(project);
            changedFields.add(QuotaChangeRequest.Field.PROJECT);
        }

        final Boolean importantRequest = bodyUpdate.getImportantRequest();
        if (isFieldChanged(importantRequest, quotaChangeRequest.isImportantRequest())) {
            updatedRequestBuilder.importantRequest(importantRequest);
            changedFields.add(QuotaChangeRequest.Field.IMPORTANT_REQUEST);
        }
    }

    @NotNull
    public List<? extends QuotaChangeRequest.ChangeAmount> getChanges() {
        return changes;
    }

    @NotNull
    public QuotaChangeRequest getUpdatedRequest() {
        return updatedRequest;
    }

    @NotNull
    public Set<QuotaChangeRequest.Field> getChangedFields() {
        return changedFields;
    }

    public boolean isChangedField(final QuotaChangeRequest.Field field) {
        return changedFields.contains(field);
    }

    protected static <T> boolean isFieldChanged(@Nullable final T updatedValue, @Nullable final T currentValue) {
        return updatedValue != null && !updatedValue.equals(currentValue);
    }

    public boolean isOnlyDecrease() {
        return onlyDecrease;
    }
}
