package com.infoclinika.pdx.controller;

import com.infoclinika.pdx.projection.AggregateGeneStatistics;
import com.infoclinika.pdx.projection.GeneNameWithOneSynonym;
import com.infoclinika.pdx.service.GeneService;
import com.infoclinika.pdx.service.annotation.PrimarySiteService;
import com.infoclinika.pdx.service.statistics.GeneStatisticsService;
import com.infoclinika.pdx.specification.FilterQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import static com.google.common.collect.Lists.newArrayList;

/**
 * Handles requests for genes.
 */
@RestController
@RequestMapping("/api/gene")
public class GeneController {
    private static final Pattern NON_WORD = Pattern.compile("[^\\w-]+");
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final GeneService geneService;
    private final GeneStatisticsService geneStatisticsService;
    private final PrimarySiteService primarySiteService;

    /**
     * Constructor to provide required services.
     */
    @Autowired
    public GeneController(GeneService geneService,
                          GeneStatisticsService geneStatisticsService,
                          PrimarySiteService primarySiteService) {
        this.geneService = geneService;
        this.geneStatisticsService = geneStatisticsService;
        this.primarySiteService = primarySiteService;
    }

    /**
     * Returns genes that contain provided name or synonym.
     */
    @GetMapping("/search")
    public List<GeneNameWithOneSynonym> searchGene(
            @RequestParam(name = "name", required = false) String name) {
        logger.debug("Request to /api/gene/search?name={}", name);
        if (name == null || name.trim().isEmpty()) {
            return newArrayList();
        }
        return geneService.findGenesLike(name);
    }

    /**
     * Returns genes map that contain provided names or synonyms.
     */
    @GetMapping("/multiple-search")
    public Map<String, List<GeneNameWithOneSynonym>> searchMultipleGene(
            @RequestParam(name = "names", required = false) String namesString) {
        logger.debug("Request to /api/gene/multiple-search?names={}", namesString);
        if (namesString == null || namesString.trim().isEmpty()) {
            return Collections.emptyMap();
        }
        final String[] split = NON_WORD.split(namesString);
        if (split.length == 0) {
            return Collections.emptyMap();
        }
        return geneService.findGenesOrSynonyms(newArrayList(split));
    }

    /**
     * Returns gene statistics by provided filter options.
     */
    @GetMapping("/statistics")
    public List<AggregateGeneStatistics> getStatistics(
            @RequestParam(name = "queries", required = false) List<FilterQuery> queries) {
        logger.debug("Request to /api/gene/statistics?queries={}", queries);

        final List<FilterQuery> filterQueries = primarySiteService.getQueriesWithIgnoredCategories(queries);
        return geneStatisticsService.getStatistics(filterQueries);
    }
}
