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

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.ParametersAreNonnullByDefault;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;

import io.swagger.annotations.Api;
import io.swagger.annotations.Authorization;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;

import ru.yandex.qe.dispenser.InstrumentedQloudTvmService;
import ru.yandex.qe.dispenser.api.v1.DiSetAmountResult;
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.quota.request.QuotaChangeRequestDao;
import ru.yandex.qe.dispenser.domain.hierarchy.HierarchySupplier;
import ru.yandex.qe.dispenser.quartz.trigger.QuartzTrackerComment;
import ru.yandex.qe.dispenser.swagger.DispenserSecurityDefinition;
import ru.yandex.qe.dispenser.swagger.SwaggerTags;
import ru.yandex.qe.dispenser.ws.Idempotent;
import ru.yandex.qe.dispenser.ws.ResourcePreorderRequestService;
import ru.yandex.qe.dispenser.ws.ResourcePreorderRequestUtils;
import ru.yandex.qe.dispenser.ws.ServiceBase;
import ru.yandex.qe.dispenser.ws.param.DiExceptionMapper;
import ru.yandex.qe.dispenser.ws.quota.request.SetResourceAmountBody;
import ru.yandex.qe.dispenser.ws.quota.request.workflow.context.PerformerContext;
import ru.yandex.qe.dispenser.ws.reqbody.SetResourceAmountBodyOptional;
import ru.yandex.qe.hitman.tvm.TvmConstants;
import ru.yandex.qe.hitman.tvm.qloud.TvmServiceTicketInfo;

@ParametersAreNonnullByDefault
@Controller
@Path("/v1/hooks/form-preorder")
@Produces(ServiceBase.APPLICATION_JSON_UTF_8)
@org.springframework.stereotype.Service("form-preorder")
@Api(tags = {SwaggerTags.DISPENSER_API}, authorizations = {@Authorization(value = DispenserSecurityDefinition.AUTHORIZATION_SCHEME_NAME)})
public class ResourcePreorderFormService extends ServiceBase {
    private final static Logger LOG = LoggerFactory.getLogger(ResourcePreorderFormService.class);

    @Value("${forms.tvm.id}")
    private long formsTvmId;

    @Autowired
    private QuotaChangeRequestDao quotaChangeRequestDao;

    @Autowired
    private ResourcePreorderRequestService resourcePreorderRequestService;

    @Autowired
    private InstrumentedQloudTvmService qloudTvmService;

    @Autowired
    private HierarchySupplier hierarchySupplier;

    @Autowired
    private QuartzTrackerComment quartzTrackerCommentTrigger;

    @Autowired
    private ResourcePreorderRequestUtils preorderRequestUtils;

    @POST
    @NotNull
    @Idempotent
    public Response create(@QueryParam("suppressSummon") @DefaultValue("false") boolean suppressSummon,
                           @RequestBody final ResourcePreorderFormBody body, @Context final HttpHeaders headers) {
        final StringBuilder messageBuilder = new StringBuilder("Ошибка при попытке выдачи квоты через форму.\n");
        try {
            return process(body, headers, suppressSummon);
        } catch (WebApplicationException e) {
            LOG.error("FormPreorder error:" + e.getMessage(), e);
            if (e.getResponse().getEntity() instanceof DiSetAmountResult.Errors) {
                final DiSetAmountResult.Errors errors = (DiSetAmountResult.Errors) e.getResponse().getEntity();
                for (final DiSetAmountResult.Errors.Item item : errors.getErrors()) {
                    messageBuilder.append("Ошибка:").append(item.getMessage()).append(" в запросах ").append(item.getProblemRequestIds()).append("\n");
                }
            } else {
                messageBuilder.append(e.getResponse().getEntity().toString());
            }
        } catch (RuntimeException e) {
            LOG.error("FormPreorder error:" + e.getMessage(), e);
            messageBuilder.append(e.getMessage());
        }
        if (suppressSummon) {
            quartzTrackerCommentTrigger.run(body.getTicket(), messageBuilder.toString());
        } else {
            quartzTrackerCommentTrigger.run(body.getTicket(), messageBuilder.toString(), body.getUser());
        }
        return Response.status(DiExceptionMapper.ExtraStatus.UNPROCESSABLE_ENTITY).build();
    }

    @NotNull
    private Response process(final ResourcePreorderFormBody body, final HttpHeaders headers,
                             final boolean suppressSummon) {
        LOG.info("FormPreorder POST-body: " + body);

        if (!checkTvm(headers)) {
            return Response.status(Response.Status.UNAUTHORIZED).build();
        }

        @NotNull final Optional<QuotaChangeRequest> quotaChangeRequestO = quotaChangeRequestDao.findByTicketKey(body.getTicket());
        if (!quotaChangeRequestO.isPresent()) {
            return Response.status(Response.Status.BAD_REQUEST)
                    .entity("Request for ticket " + body.getTicket() + " not found")
                    .header("Content-Type", "text/plain; charset=utf-8")
                    .build();
        }
        final QuotaChangeRequest quotaChangeRequest = quotaChangeRequestO.get();

        final List<QuotaChangeRequest.Change> serviceChanges = quotaChangeRequest.getChanges()
                .stream()
                .filter(c -> c.getResource().getService().getName().equals(body.getService()))
                .collect(Collectors.toList());

        if (serviceChanges.isEmpty()) {
            throw new IllegalArgumentException("Сервис из формы (" + body.getService() + ") отличается от сервисов из запроса");
        }

        final SetResourceAmountBody resourceAmountBody;

        if (body.isAllocateAll()) {

            final List<SetResourceAmountBodyOptional.ChangeBody> changeBodies = serviceChanges.stream()
                    .map(c -> new SetResourceAmountBodyOptional.ChangeBody(c.getResource().getService().getKey(),
                            c.getResource().getPublicKey(),
                            c.getSegments().stream().map(Segment::getPublicKey).collect(Collectors.toSet()),
                            c.getBigOrder().getId()))
                    .collect(Collectors.toList());

            resourceAmountBody = preorderRequestUtils.getFullBody(
                    new SetResourceAmountBodyOptional(Collections.singletonList(
                            new SetResourceAmountBodyOptional.Item(quotaChangeRequest.getId(), null, changeBodies, getComment(body))
                    ), SetResourceAmountBodyOptional.UpdateFor.BOTH)
            );
        } else {
            final List<SetResourceAmountBody.ChangeBody> changeBodies = ResourcePreorderFormUtils.parseQuota(body, serviceChanges);
            final SetResourceAmountBody.Item item = new SetResourceAmountBody.Item(quotaChangeRequest.getId(), null, changeBodies, getComment(body));
            final List<SetResourceAmountBody.Item> updates = new ArrayList<>();
            updates.add(item);
            resourceAmountBody = new SetResourceAmountBody(updates);
        }

        final Person person = hierarchySupplier.get().getPersonReader().readPersonByLogin(body.getUser());
        final PerformerContext context = new PerformerContext(person);

        resourcePreorderRequestService.setResourceQuotaStateInContext(resourceAmountBody, context, suppressSummon);

        return Response.ok("OK").build();
    }

    private String getComment(final ResourcePreorderFormBody body) {
        return body.getComment()
                + (body.getComment().isEmpty() || body.getAdvanceTicket().isEmpty() ? "" : "\n----\n")
                + (body.getAdvanceTicket().isEmpty() ? "" : "Тикет с запросом аванса " + body.getAdvanceTicket());
    }

    private boolean checkTvm(final HttpHeaders headers) {
        final String serviceTicket = headers.getHeaderString(TvmConstants.TVM_SERVICE_HEADER_NAME);
        if (serviceTicket == null) {
            return false;
        }
        final Optional<TvmServiceTicketInfo> validatedTicketO = qloudTvmService.validateServiceTicket(serviceTicket);
        if (!validatedTicketO.isPresent()) {
            return false;
        }
        final int requestSource = validatedTicketO.get().getSource();
        return requestSource == formsTvmId;
    }
}
