package ru.yandex.qe.dispenser.ws;

import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.ParametersAreNonnullByDefault;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

import io.swagger.annotations.Api;
import io.swagger.annotations.Authorization;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

import ru.yandex.qe.dispenser.api.v1.DiSegmentation;
import ru.yandex.qe.dispenser.api.v1.response.DiListResponse;
import ru.yandex.qe.dispenser.domain.CampaignResource;
import ru.yandex.qe.dispenser.domain.Segment;
import ru.yandex.qe.dispenser.domain.Segmentation;
import ru.yandex.qe.dispenser.domain.dao.campaign.CampaignResourceDao;
import ru.yandex.qe.dispenser.domain.dao.segment.SegmentDao;
import ru.yandex.qe.dispenser.domain.dao.segmentation.SegmentationDao;
import ru.yandex.qe.dispenser.domain.hierarchy.Hierarchy;
import ru.yandex.qe.dispenser.domain.index.LongIndexBase;
import ru.yandex.qe.dispenser.swagger.DispenserSecurityDefinition;
import ru.yandex.qe.dispenser.swagger.SwaggerTags;
import ru.yandex.qe.dispenser.ws.reqbody.SegmentationBody;

@ParametersAreNonnullByDefault
@Controller
@Path("/v1/segmentations")
@Produces(ServiceBase.APPLICATION_JSON_UTF_8)
@org.springframework.stereotype.Service("segmentation")
@Api(tags = {SwaggerTags.DISPENSER_API}, authorizations = {@Authorization(value = DispenserSecurityDefinition.AUTHORIZATION_SCHEME_NAME)})
public class SegmentationService extends ServiceBase {

    public static final String SEGMENTATION_KEY = "segmentation";

    @Autowired
    private SegmentationDao segmentationDao;
    @Autowired
    private SegmentDao segmentDao;
    @Autowired
    private CampaignResourceDao campaignResourceDao;

    @GET
    @NotNull
    public DiListResponse<DiSegmentation> getSegmentations() {
        final Set<Segmentation> segmentations = Hierarchy.get().getSegmentationReader().getAll();

        return new DiListResponse<>(segmentations.stream()
                .map(Segmentation::toView)
                .collect(Collectors.toList()));
    }

    @GET
    @NotNull
    @Path("/{" + SEGMENTATION_KEY + "}")
    public DiSegmentation getSegmentation(@PathParam(SEGMENTATION_KEY) final Segmentation segmentation) {
        return segmentation.toView();
    }

    @POST
    @Access(dispenserAdmin = true)
    @NotNull
    public DiSegmentation createSegmentation(final SegmentationBody body) {

        final Segmentation segmentation = segmentationDao.create(new Segmentation.Builder(body.getKey())
                .name(body.getName())
                .description(body.getDescription())
                .priority(body.getPriority())
                .build());

        return segmentation.toView();
    }

    @DELETE
    @Access(dispenserAdmin = true)
    @Path("/{" + SEGMENTATION_KEY + "}")
    public DiSegmentation deleteSegmentation(@PathParam(SEGMENTATION_KEY) final Segmentation segmentation) {
        validateCampaignResources(segmentation);
        segmentationDao.delete(segmentation);
        return segmentation.toView();
    }

    @PUT
    @Access(dispenserAdmin = true)
    @Path("/{" + SEGMENTATION_KEY + "}")
    @NotNull
    public DiSegmentation updateSegmentation(@PathParam(SEGMENTATION_KEY) final Segmentation segmentation, final SegmentationBody body) {
        final Segmentation modifiedSegmentation = new Segmentation.Builder(segmentation.getKey().getPublicKey())
                .name(body.getName())
                .description(body.getDescription())
                .priority(body.getPriority())
                .build();

        segmentationDao.update(modifiedSegmentation);

        return modifiedSegmentation.toView();
    }

    private void validateCampaignResources(@NotNull final Segmentation segmentation) {
        final Set<CampaignResource> allCampaignResources = campaignResourceDao.getAll();
        final Set<Segment> segmentationSegments = segmentDao.get(segmentation);
        final Set<Long> segmentationSegmentIds = segmentationSegments.stream()
                .map(LongIndexBase::getId).collect(Collectors.toSet());
        final boolean segmentationIsUsed = allCampaignResources.stream().anyMatch(cr -> {
            final boolean hasSegmentation = cr.getSettings().getSegmentations().stream()
                    .anyMatch(s -> s.getId() == segmentation.getId());
            final boolean hasSegment = cr.getSettings().getSegmentsBigOrders().stream()
                    .anyMatch(s -> s.getSegments().stream().anyMatch(segmentationSegmentIds::contains));
            return hasSegmentation || hasSegment;
        });
        if (segmentationIsUsed) {
            throw new IllegalArgumentException("This segmentation is referenced by one or more campaign settings.");
        }
    }

}
