package io.delphiplatform.api.v3.rdb.service;

import com.google.common.collect.ImmutableMap;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

import io.delphiplatform.api.v3.model.gras.Project;
import io.delphiplatform.api.v3.rdb.entity.ProjectEntity;
import io.delphiplatform.api.v3.rdb.repository.ProjectRepository;
import io.delphiplatform.api.v3.rdb.service.specification.SpecificationProvider;
import io.delphiplatform.api.v3.view.util.Params;

@Service
public class ProjectService extends EntityService<ProjectEntity> {

    private static final Set<String> FETCHES = Set
        .of("recProjectType", "repertoireOwner", "raasInitCompany", "label");

    private final ProjectRepository projectRepository;
    private final SpecificationProvider<ProjectEntity> specificationProvider;

    private static final ImmutableMap<String, String> SORT_BY_TO_ENTITY_FIELD_MAP = ImmutableMap.<String, String>builder()
        .put("label_id", "label_label_id")
        .put("project_number", "rec_project_number")
        .put("project_title", "rec_project_title")
        .put("project_type", "rec_project_type_rec_project_type")
        .put("project_type_name", "rec_project_type_rec_project_type_name")
        .put("raas_init_key", "raas_init_company_company_key")
        .put("rep_owner_key", "repertoire_owner_rep_owner_key")
        .build();

    @Autowired
    public ProjectService(ProjectRepository projectRepository,
        SpecificationProvider<ProjectEntity> specificationProvider) {
        super(projectRepository, SORT_BY_TO_ENTITY_FIELD_MAP);
        this.specificationProvider = specificationProvider;
        this.projectRepository = projectRepository;
    }


    @Transactional(readOnly = true)
    public Project findOne(String projectId) {
        return projectRepository.findById(projectId)
            .map(Project::from)
            .orElseThrow(NOT_FOUND);
    }

    @Transactional(readOnly = true)
    public List<Project> find(Params params) {

        Specification<ProjectEntity> specification = and(Arrays.asList(
            specificationProvider.greaterThanOrEqualToValueSpec("raasInitDate", params.getStartDate()),
            specificationProvider.lessThanOrEqualToValueSpec("raasInitDate", params.getEndDate()),

            specificationProvider.singleValueSpec("artistId", params.getArtistId(), "artists"),
            specificationProvider.singleValueSpec("prodVersNo", params.getProductVersionNo(), "productVersions"),
            specificationProvider.multiValueSpec("repOwnerKey", params.getRepOwnerKey(), "repertoireOwner"),
            specificationProvider.multiValueSpec("productId", params.getProductIds(),
                Arrays.asList("productVersions", "products")),

            specificationProvider.fetchesSpec(FETCHES)
        ));

        return find(specification, getPageRequestSortByToCamelCase(params))
            .stream()
            .map(Project::from)
            .collect(Collectors.toList());
    }
}
