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

import java.net.MalformedURLException;
import java.net.URL;
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.VariantSetAvailableFormatRecord;
import org.brapi.test.BrAPITestServer.model.dto.geno.VariantSetAvailableFormatWriteRequest;
import org.brapi.test.BrAPITestServer.model.entity.geno.VariantSetAvailableFormatEntity;
import org.brapi.test.BrAPITestServer.model.entity.geno.VariantSetEntity;
import org.brapi.test.BrAPITestServer.repository.geno.VariantSetAvailableFormatRepository;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import io.swagger.model.geno.GenoFileDataFormatEnum;

@Service
public class VariantSetAvailableFormatService {

	private final VariantSetAvailableFormatRepository variantSetAvailableFormatRepository;
	private final VariantSetService variantSetService;

	public VariantSetAvailableFormatService(
			VariantSetAvailableFormatRepository variantSetAvailableFormatRepository,
			VariantSetService variantSetService) {
		this.variantSetAvailableFormatRepository = variantSetAvailableFormatRepository;
		this.variantSetService = variantSetService;
	}

	public List<VariantSetAvailableFormatRecord> findFormatsByVariantSet(String variantSetDbId)
			throws BrAPIServerException {
		variantSetService.getVariantSetEntity(variantSetDbId);
		return variantSetAvailableFormatRepository.findByVariantSet_Id(variantSetDbId).stream()
				.map(this::convertFromEntity)
				.collect(Collectors.toList());
	}

	public VariantSetAvailableFormatRecord saveFormat(String variantSetDbId,
			VariantSetAvailableFormatWriteRequest request) throws BrAPIServerException {
		VariantSetEntity variantSet = variantSetService.getVariantSetEntity(variantSetDbId);
		validateFormatRequest(request);
		if (request.getFormatDbId() != null
				&& variantSetAvailableFormatRepository.findById(request.getFormatDbId()).isPresent()) {
			throw new BrAPIServerException(HttpStatus.CONFLICT,
					"Available format already exists: " + request.getFormatDbId());
		}
		VariantSetAvailableFormatEntity entity = new VariantSetAvailableFormatEntity();
		if (request.getFormatDbId() != null && !request.getFormatDbId().isBlank()) {
			entity.setId(request.getFormatDbId().trim());
		}
		updateEntity(entity, variantSet, request);
		return convertFromEntity(variantSetAvailableFormatRepository.save(entity));
	}

	public VariantSetAvailableFormatRecord updateFormat(String variantSetDbId, String formatDbId,
			VariantSetAvailableFormatWriteRequest request) throws BrAPIServerException {
		VariantSetAvailableFormatEntity entity = getFormatEntity(variantSetDbId, formatDbId);
		validateFormatRequest(request);
		updateEntity(entity, entity.getVariantSet(), request);
		return convertFromEntity(variantSetAvailableFormatRepository.save(entity));
	}

	public VariantSetAvailableFormatRecord deleteFormat(String variantSetDbId, String formatDbId)
			throws BrAPIServerException {
		VariantSetAvailableFormatEntity entity = getFormatEntity(variantSetDbId, formatDbId);
		VariantSetAvailableFormatRecord deleted = convertFromEntity(entity);
		try {
			variantSetAvailableFormatRepository.delete(entity);
			variantSetAvailableFormatRepository.flush();
		} catch (DataIntegrityViolationException e) {
			throw new BrAPIServerException(HttpStatus.CONFLICT, "Available format cannot be deleted");
		}
		return deleted;
	}

	private VariantSetAvailableFormatEntity getFormatEntity(String variantSetDbId, String formatDbId)
			throws BrAPIServerException {
		VariantSetAvailableFormatEntity entity = variantSetAvailableFormatRepository.findById(formatDbId).orElseThrow(
				() -> new BrAPIServerDbIdNotFoundException("availableFormat", formatDbId, HttpStatus.NOT_FOUND));
		if (entity.getVariantSet() == null || !variantSetDbId.equals(entity.getVariantSet().getId())) {
			throw new BrAPIServerDbIdNotFoundException("availableFormat", formatDbId, HttpStatus.NOT_FOUND);
		}
		return entity;
	}

	private void validateFormatRequest(VariantSetAvailableFormatWriteRequest request) throws BrAPIServerException {
		if (request.getDataFormat() == null || request.getDataFormat().isBlank()) {
			throw new BrAPIServerException(HttpStatus.BAD_REQUEST, "dataFormat is required");
		}
		if (request.getFileFormat() == null || request.getFileFormat().isBlank()) {
			throw new BrAPIServerException(HttpStatus.BAD_REQUEST, "fileFormat is required");
		}
		if (GenoFileDataFormatEnum.fromValue(request.getDataFormat()) == null) {
			throw new BrAPIServerException(HttpStatus.BAD_REQUEST, "Invalid dataFormat: " + request.getDataFormat());
		}
		if (request.getFileURL() != null && !request.getFileURL().isBlank()) {
			try {
				new URL(request.getFileURL().trim());
			} catch (MalformedURLException e) {
				throw new BrAPIServerException(HttpStatus.BAD_REQUEST, "Invalid fileURL: " + request.getFileURL());
			}
		}
	}

	private void updateEntity(VariantSetAvailableFormatEntity entity, VariantSetEntity variantSet,
			VariantSetAvailableFormatWriteRequest request) {
		entity.setVariantSet(variantSet);
		entity.setDataFormat(GenoFileDataFormatEnum.fromValue(request.getDataFormat()));
		entity.setFileFormat(io.swagger.model.WSMIMEDataTypes.fromValue(request.getFileFormat()));
		entity.setFileURL(trimToNull(request.getFileURL()));
		entity.setExpandHomozygotes(request.getExpandHomozygotes());
		entity.setSepPhased(trimToNull(request.getSepPhased()));
		entity.setSepUnphased(trimToNull(request.getSepUnphased()));
		entity.setUnknownString(trimToNull(request.getUnknownString()));
	}

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

	private VariantSetAvailableFormatRecord convertFromEntity(VariantSetAvailableFormatEntity entity) {
		VariantSetAvailableFormatRecord record = new VariantSetAvailableFormatRecord();
		record.setFormatDbId(entity.getId());
		if (entity.getVariantSet() != null) {
			record.setVariantSetDbId(entity.getVariantSet().getId());
		}
		if (entity.getDataFormat() != null) {
			record.setDataFormat(entity.getDataFormat().toString());
		}
		if (entity.getFileFormat() != null) {
			record.setFileFormat(entity.getFileFormat().toString());
		}
		record.setFileURL(entity.getFileURL());
		record.setExpandHomozygotes(entity.getExpandHomozygotes());
		record.setSepPhased(entity.getSepPhased());
		record.setSepUnphased(entity.getSepUnphased());
		record.setUnknownString(entity.getUnknownString());
		return record;
	}
}
