package ru.yandex.qe.dispenser.ws.allocation;

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import com.google.common.collect.ImmutableList;

import ru.yandex.qe.dispenser.api.util.SerializationUtils;
import ru.yandex.qe.dispenser.api.v1.DiAmount;
import ru.yandex.qe.dispenser.client.v1.impl.DispenserConfig;
import ru.yandex.qe.dispenser.domain.MessageHelper;
import ru.yandex.qe.dispenser.domain.Person;
import ru.yandex.qe.dispenser.domain.QuotaChangeRequest;
import ru.yandex.qe.dispenser.domain.Segment;
import ru.yandex.qe.dispenser.domain.dao.property.PropertyReader;
import ru.yandex.qe.dispenser.domain.exception.SingleMessageException;
import ru.yandex.qe.dispenser.domain.hierarchy.Hierarchy;
import ru.yandex.qe.dispenser.domain.property.Property;
import ru.yandex.qe.dispenser.ws.quota.request.ChangeComparator;
import ru.yandex.qe.dispenser.ws.quota.request.SetResourceAmountBody;
import ru.yandex.qe.dispenser.ws.quota.request.ticket.QuotaChangeRequestTicketManager;
import ru.yandex.qe.dispenser.domain.tracker.TrackerManager;
import ru.yandex.qe.dispenser.ws.bot.Provider;
import ru.yandex.startrek.client.model.CollectionUpdate;
import ru.yandex.startrek.client.model.CommentCreate;
import ru.yandex.startrek.client.model.IssueUpdate;

public abstract class AssigneeAndSummonRequestAllocationWorkflow extends ResourceRequestAllocationWorkflow {

    final TrackerManager trackerManager;
    final MessageHelper messageHelper;
    final DispenserConfig.Environment environment;
    final String clusterPrefix;
    final String providerPropertyKey;
    final String assigneeDefault;
    final Boolean needToSummon;

    @SuppressWarnings("ConstructorWithTooManyParameters")
    protected AssigneeAndSummonRequestAllocationWorkflow(final Provider provider,
                                                         final TrackerManager trackerManager,
                                                         final MessageHelper messageHelper,
                                                         final DispenserConfig.Environment environment,
                                                         final String clusterPrefix,
                                                         final String providerPropertyKey,
                                                         final String assigneeDefault) {
        super(provider);

        this.trackerManager = trackerManager;
        this.messageHelper = messageHelper;
        this.environment = environment;
        this.clusterPrefix = clusterPrefix;
        this.providerPropertyKey = providerPropertyKey;
        this.assigneeDefault = assigneeDefault;
        this.needToSummon = true;
    }

    @SuppressWarnings("ConstructorWithTooManyParameters")
    protected AssigneeAndSummonRequestAllocationWorkflow(final Provider provider,
                                                         final TrackerManager trackerManager,
                                                         final MessageHelper messageHelper,
                                                         final DispenserConfig.Environment environment,
                                                         final String clusterPrefix,
                                                         final String providerPropertyKey,
                                                         final String assigneeDefault,
                                                         final Boolean needToSummon) {
        super(provider);
        this.trackerManager = trackerManager;
        this.messageHelper = messageHelper;
        this.environment = environment;
        this.clusterPrefix = clusterPrefix;
        this.providerPropertyKey = providerPropertyKey;
        this.assigneeDefault = assigneeDefault;
        this.needToSummon = needToSummon;
    }

    @Override
    public void onAllocationRequest(final QuotaChangeRequest request, final Collection<QuotaChangeRequest.Change> changes,
                                    final Person performer, final boolean suppressSummon) {
        final String issueKey = request.getTrackerIssueKey();
        if (issueKey == null) {
            throw SingleMessageException.illegalArgument("cannot.allocate.without.ticket");
        }

        final PropertyReader propertyReader = Hierarchy.get().getPropertyReader();

        final String assignee = propertyReader.read(providerPropertyKey + "Workflow", "allocatorPerson")
                .map(p -> p.getValue().getAs(Property.Type.STRING)).orElse(assigneeDefault);

        final String comment = formatAllocateComment(request, changes, performer, assignee);

        final IssueUpdate update = IssueUpdate
                .assignee(assignee)
                .update("followers", CollectionUpdate.add(performer.getLogin()))
                .comment(getComment(assignee, comment, suppressSummon))
                .build();

        trackerManager.updateIssue(issueKey, update);
    }

    private CommentCreate getComment(final String assignee, final String comment, final boolean suppressSummon) {
        final CommentCreate.Builder commentBuilder = CommentCreate.comment(comment);
        return (needToSummon && !suppressSummon)
                ? commentBuilder.summonees(assignee).build()
                : commentBuilder.build();
    }

    String formatAllocateComment(final QuotaChangeRequest request, final Collection<QuotaChangeRequest.Change> changes, final Person performer, final String assignee) {
        boolean singleProvider = isSingleProvider(changes);
        String providerPrefix = providerPrefix(singleProvider, changes);
        final List<List<String>> resources = QuotaChangeRequestTicketManager.formatChanges(changes,
                QuotaChangeRequest.Change::getAmountReady, !singleProvider);

        final String resourcesTable = QuotaChangeRequestTicketManager.toWikiTableWithBoldResourceSummary(resources);

        final List<SetResourceAmountBody.ChangeBody> changeBodies = changes.stream()
                .sorted(ChangeComparator.INSTANCE)
                .map(c -> new SetResourceAmountBody.ChangeBody(
                        c.getResource().getService().getKey(),
                        c.getKey().getBigOrder() == null ? null : c.getKey().getBigOrder().getId(),
                        c.getResource().getPublicKey(),
                        c.getSegments().stream().map(Segment::getPublicKey).collect(Collectors.toSet()),
                        null,
                        DiAmount.of(c.getAmountReady(), c.getResource().getType().getBaseUnit())))
                .collect(Collectors.toList());

        final SetResourceAmountBody body = new SetResourceAmountBody(ImmutableList.of(
                new SetResourceAmountBody.Item(request.getId(), null, changeBodies, "")
        ));

        return getFormat(performer, assignee, providerPrefix + resourcesTable, body);
    }

    private String providerPrefix(boolean singleProvider, Collection<QuotaChangeRequest.Change> changes) {
        if (!singleProvider) {
            return "";
        }
        String providerName = changes.iterator().next().getResource().getService().getName();
        return messageHelper.format("quota.request.preorder.ticket.provider.comment", providerName) + "\n";
    }

    private boolean isSingleProvider(Collection<QuotaChangeRequest.Change> changes) {
        return changes.stream().map(c -> c.getResource().getService().getId()).collect(Collectors.toSet()).size() == 1;
    }

    String getFormat(final Person performer, final String assignee, final String resourcesTable, final SetResourceAmountBody body) {
        return messageHelper.format("quota.request.preorder.ticket.allocate.comment." + providerPropertyKey,
                performer.getLogin(),
                resourcesTable,
                assignee,
                environment.getDispenserHost() + clusterPrefix,
                SerializationUtils.writeValueAsString(body)
        );
    }
}
