package org.brapi.test.BrAPITestServer.service.geno;

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

import org.brapi.test.BrAPITestServer.exceptions.BrAPIServerDbIdNotFoundException;
import org.brapi.test.BrAPITestServer.exceptions.BrAPIServerException;
import org.brapi.test.BrAPITestServer.model.dto.geno.VariantSetAnalysisWriteRequest;
import org.brapi.test.BrAPITestServer.model.entity.geno.VariantSetAnalysisEntity;
import org.brapi.test.BrAPITestServer.model.entity.geno.VariantSetEntity;
import org.brapi.test.BrAPITestServer.repository.geno.VariantSetAnalysisRepository;
import org.brapi.test.BrAPITestServer.service.DateUtility;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import io.swagger.model.geno.Analysis;

@Service
public class VariantSetAnalysisService {

	private final VariantSetAnalysisRepository variantSetAnalysisRepository;
	private final VariantSetService variantSetService;

	public VariantSetAnalysisService(VariantSetAnalysisRepository variantSetAnalysisRepository,
			VariantSetService variantSetService) {
		this.variantSetAnalysisRepository = variantSetAnalysisRepository;
		this.variantSetService = variantSetService;
	}

	public List<Analysis> findAnalysisByVariantSet(String variantSetDbId) throws BrAPIServerException {
		assertVariantSetExists(variantSetDbId);
		return variantSetAnalysisRepository.findByVariantSet_Id(variantSetDbId).stream()
				.map(this::convertFromEntity)
				.collect(Collectors.toList());
	}

	public Analysis saveAnalysis(String variantSetDbId, VariantSetAnalysisWriteRequest request)
			throws BrAPIServerException {
		VariantSetEntity variantSet = variantSetService.getVariantSetEntity(variantSetDbId);
		if (request.getAnalysisName() == null || request.getAnalysisName().isBlank()) {
			throw new BrAPIServerException(HttpStatus.BAD_REQUEST, "analysisName is required");
		}
		if (request.getAnalysisDbId() != null
				&& variantSetAnalysisRepository.findById(request.getAnalysisDbId()).isPresent()) {
			throw new BrAPIServerException(HttpStatus.CONFLICT,
					"Analysis already exists: " + request.getAnalysisDbId());
		}
		VariantSetAnalysisEntity entity = new VariantSetAnalysisEntity();
		if (request.getAnalysisDbId() != null && !request.getAnalysisDbId().isBlank()) {
			entity.setId(request.getAnalysisDbId().trim());
		}
		updateEntity(entity, variantSet, request);
		return convertFromEntity(variantSetAnalysisRepository.save(entity));
	}

	public Analysis updateAnalysis(String variantSetDbId, String analysisDbId, VariantSetAnalysisWriteRequest request)
			throws BrAPIServerException {
		VariantSetAnalysisEntity entity = getAnalysisEntity(variantSetDbId, analysisDbId);
		if (request.getAnalysisName() == null || request.getAnalysisName().isBlank()) {
			throw new BrAPIServerException(HttpStatus.BAD_REQUEST, "analysisName is required");
		}
		updateEntity(entity, entity.getVariantSet(), request);
		return convertFromEntity(variantSetAnalysisRepository.save(entity));
	}

	public Analysis deleteAnalysis(String variantSetDbId, String analysisDbId) throws BrAPIServerException {
		VariantSetAnalysisEntity entity = getAnalysisEntity(variantSetDbId, analysisDbId);
		Analysis deleted = convertFromEntity(entity);
		try {
			variantSetAnalysisRepository.delete(entity);
			variantSetAnalysisRepository.flush();
		} catch (DataIntegrityViolationException e) {
			throw new BrAPIServerException(HttpStatus.CONFLICT, "Analysis cannot be deleted");
		}
		return deleted;
	}

	private void assertVariantSetExists(String variantSetDbId) throws BrAPIServerException {
		variantSetService.getVariantSetEntity(variantSetDbId);
	}

	private VariantSetAnalysisEntity getAnalysisEntity(String variantSetDbId, String analysisDbId)
			throws BrAPIServerException {
		VariantSetAnalysisEntity entity = variantSetAnalysisRepository.findById(analysisDbId).orElseThrow(
				() -> new BrAPIServerDbIdNotFoundException("analysis", analysisDbId, HttpStatus.NOT_FOUND));
		if (entity.getVariantSet() == null || !variantSetDbId.equals(entity.getVariantSet().getId())) {
			throw new BrAPIServerDbIdNotFoundException("analysis", analysisDbId, HttpStatus.NOT_FOUND);
		}
		return entity;
	}

	private void updateEntity(VariantSetAnalysisEntity entity, VariantSetEntity variantSet,
			VariantSetAnalysisWriteRequest request) throws BrAPIServerException {
		entity.setVariantSet(variantSet);
		entity.setAnalysisName(request.getAnalysisName().trim());
		entity.setDescription(trimToNull(request.getDescription()));
		entity.setType(trimToNull(request.getType()));
		entity.setCreated(DateUtility.toDate(request.getCreated()));
		entity.setUpdated(DateUtility.toDate(request.getUpdated()));
		entity.setSoftware(normalizeSoftware(request.getSoftware()));
	}

	private List<String> normalizeSoftware(List<String> software) {
		if (software == null) {
			return new ArrayList<>();
		}
		return software.stream()
				.map(this::trimToNull)
				.filter(item -> item != null && !item.isBlank())
				.collect(Collectors.toList());
	}

	private String trimToNull(String value) {
		if (value == null) {
			return null;
		}
		String trimmed = value.trim();
		return trimmed.isEmpty() ? null : trimmed;
	}

	private Analysis convertFromEntity(VariantSetAnalysisEntity entity) {
		Analysis analysis = new Analysis();
		analysis.setAnalysisDbId(entity.getId());
		analysis.setAnalysisName(entity.getAnalysisName());
		analysis.setCreated(DateUtility.toOffsetDateTime(entity.getCreated()));
		analysis.setDescription(entity.getDescription());
		analysis.setSoftware(entity.getSoftware());
		analysis.setType(entity.getType());
		analysis.setUpdated(DateUtility.toOffsetDateTime(entity.getUpdated()));
		return analysis;
	}
}
