package com.sonymusic.dx.dxuiservice.service;

import com.sonymusic.dx.dxuiservice.dto.*;
import com.sonymusic.dx.dxuiservice.model.RecordingProjectModel;
import com.sonymusic.dx.persistence.gras.*;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.*;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Service;
import org.springframework.web.ErrorResponseException;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class RecordingProjectService {

	@PersistenceContext(name = "dx-gras-persistence")
	private EntityManager em;

	public RecordingProjectModel getRecordingProjectByProjectNo(String projectNo) {
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<RecordingProjectDTO> cq1 = cb.createQuery(RecordingProjectDTO.class);
		Root<RecordingProject> root = cq1.from(RecordingProject.class);
		List<Predicate> predicates = new ArrayList<>();
		predicates.add(cb.equal(root.get("recordingProjectNumber"), projectNo));
		cb.and(predicates.toArray(new Predicate[0]));
		return getRecordingProjectModel(root, cq1, cb, predicates);

	}

	public RecordingProjectModel getRecordingProjectByProjectId(String projectId) {
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<RecordingProjectDTO> cq1 = cb.createQuery(RecordingProjectDTO.class);
		Root<RecordingProject> root = cq1.from(RecordingProject.class);
		List<Predicate> predicates = new ArrayList<>();
		predicates.add(cb.equal(root.get("recordingProjectId"), projectId));
		cb.and(predicates.toArray(new Predicate[0]));
		return getRecordingProjectModel(root, cq1, cb, predicates);
	}

	private RecordingProjectModel getRecordingProjectModel(Root<RecordingProject> root,
			CriteriaQuery<RecordingProjectDTO> cq1, CriteriaBuilder cb, List<Predicate> predicates) {
		Join<RecordingProject, RecordingProjectType> recordingProjectRecordingProjectTypeJoin = root
				.join("recordingProjectType", JoinType.LEFT);
		Join<RecordingProject, Label> recordingProjectLabelJoin = root.join("label", JoinType.LEFT);
		Join<RecordingProject, Genre> recordingProjectGenreJoin = root.join("genre", JoinType.LEFT);
		Join<RecordingProject, Company> recordingProjectCompanyDTOJoin = root.join("repertoireOwner", JoinType.LEFT);
		cq1.select(cb.construct(RecordingProjectDTO.class,
                root.get("recordingProjectId"),
				root.get("recordingProjectTitle"),
                root.get("recordingProjectNumber"),
				cb.construct(ProjectTypeDTO.class,
						recordingProjectRecordingProjectTypeJoin.get("recordingProjectTypeKey"),
						recordingProjectRecordingProjectTypeJoin.get("recordingProjectTypeName")),
				root.get("origRecordingProjectNumber"),
				cb.construct(GenreDTO.class,
                        recordingProjectGenreJoin.get("genreKey"),
						recordingProjectGenreJoin.get("genreName")),
				root.get("repertoireOwner").get("companyName"),
                root.get("pLine"),
                cb.construct(LabelDTO.class,
						recordingProjectLabelJoin.get("labelKey"),
                        recordingProjectLabelJoin.get("labelName")),
                root.get("isConfidential")));
		cq1.where(predicates.toArray(new Predicate[0]));
		cq1.distinct(true);
		List<RecordingProjectDTO> projectDTOS = em.createQuery(cq1).getResultList();
		if (projectDTOS.isEmpty()) {
			throw new ErrorResponseException(HttpStatusCode.valueOf(400));
		}
		List<Long> projectIds = projectDTOS.stream().map(RecordingProjectDTO::getRecordingProjectId)
				.filter(Objects::nonNull).toList();
		CriteriaQuery<Tuple> pq = cb.createTupleQuery();
		Root<RecordingProject> pr = pq.from(RecordingProject.class);
		Join<RecordingProject, RecordingProjectParticipant> rpp = pr.join("recordingProjectParticipants",
				JoinType.LEFT);
		Join<RecordingProjectParticipant, Participant> part = rpp.join("participant", JoinType.LEFT);
		pq.multiselect(pr.get("recordingProjectId").alias("projectKey"), part.get("fullName").alias("fullName"));
		pq.where(pr.get("recordingProjectId").in(projectIds), cb.isTrue(rpp.get("isProductMainArtist")));

		List<Tuple> rows = em.createQuery(pq).getResultList();
		Map<Long, List<String>> nameByProject = rows.stream()
				.map(t -> Map.entry((Long) t.get("projectKey"), (String) t.get("fullName")))
				.filter(e -> e.getValue() != null && !e.getValue().isBlank()).collect(Collectors
						.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
		for (RecordingProjectDTO dto : projectDTOS) {
			List<String> names = nameByProject.getOrDefault(dto.getRecordingProjectId(), Collections.emptyList());
			String joined = names.stream().distinct().collect(Collectors.joining(", "));
			dto.setMainArtist(joined);
		}
		RecordingProjectDTO projectDTO = projectDTOS.get(0);

        if (!projectDTO.isConfidential()) {
            RecordingProjectModel recordingProjectModel = new RecordingProjectModel();
            recordingProjectModel.setProjectId(projectDTO.getRecordingProjectId());
            recordingProjectModel.setProjectTitle(projectDTO.getProjectTitle());
            recordingProjectModel.setProjectNumber(projectDTO.getProjectNumber());
            recordingProjectModel.setProjectType(projectDTO.getProjectType().getRecordingProjectTypeName());
            recordingProjectModel.setOrigProjectNo(projectDTO.getOrigRecordingProjectNo());
            recordingProjectModel.setMainArtist(projectDTO.getMainArtist());
            recordingProjectModel.setGenre(projectDTO.getGlobalGenre().getGenreName());
            recordingProjectModel.setRepertoireOwner(projectDTO.getRepertoireOwner());
            recordingProjectModel.setpLine(projectDTO.getpLine());
            recordingProjectModel.setImprintLabel(projectDTO.getImprintLabel().getLabelName());
            return recordingProjectModel;
        }else{
            RecordingProjectModel recordingProjectModel = new RecordingProjectModel();
            recordingProjectModel.setProjectNumber(projectDTO.getProjectNumber());
            recordingProjectModel.setConfidential(projectDTO.isConfidential());
            recordingProjectModel.setProjectType("ConfidentialProject");
            return recordingProjectModel;
        }
	}
}
