package at.procon.dip.clustering.service;

import at.procon.dip.clustering.dto.ClusterSetResponse;
import at.procon.dip.clustering.dto.CreateClusterSetRequest;
import at.procon.dip.clustering.dto.EmbeddingSelectionSpec;
import at.procon.dip.clustering.dto.UpdateClusterSetRequest;
import at.procon.dip.clustering.entity.EmbeddingClusterSet;
import at.procon.dip.clustering.repository.EmbeddingClusterSetRepository;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

@Service
@RequiredArgsConstructor
public class EmbeddingClusterSetService {

    private final EmbeddingClusterSetRepository clusterSetRepository;
    private final ObjectMapper objectMapper;

    @Transactional
    public ClusterSetResponse create(CreateClusterSetRequest request) {
        if (clusterSetRepository.existsByCodeIgnoreCase(request.code())) {
            throw new ResponseStatusException(HttpStatus.CONFLICT,
                    "Cluster set code already exists: " + request.code());
        }
        EmbeddingClusterSet saved = clusterSetRepository.save(EmbeddingClusterSet.builder()
                .code(request.code().trim())
                .name(request.name().trim())
                .description(request.description())
                .selectionJson(writeJson(request.selection()))
                .active(request.active() == null || request.active())
                .build());
        return toResponse(saved);
    }

    @Transactional
    public ClusterSetResponse update(UUID id, UpdateClusterSetRequest request) {
        EmbeddingClusterSet existing = clusterSetRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Cluster set not found: " + id));
        existing.setName(request.name().trim());
        existing.setDescription(request.description());
        existing.setSelectionJson(writeJson(request.selection()));
        existing.setActive(request.active() == null || request.active());
        return toResponse(clusterSetRepository.save(existing));
    }

    public ClusterSetResponse get(UUID id) {
        return toResponse(clusterSetRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Cluster set not found: " + id)));
    }

    public List<ClusterSetResponse> list(Boolean activeOnly) {
        List<EmbeddingClusterSet> sets = activeOnly == null
                ? clusterSetRepository.findAllByOrderByCodeAsc()
                : clusterSetRepository.findAllByActiveOrderByCodeAsc(activeOnly);
        return sets.stream().map(this::toResponse).toList();
    }

    private ClusterSetResponse toResponse(EmbeddingClusterSet entity) {
        return new ClusterSetResponse(
                entity.getId(),
                entity.getCode(),
                entity.getName(),
                entity.getDescription(),
                entity.isActive(),
                readSelection(entity.getSelectionJson()),
                entity.getCreatedAt(),
                entity.getUpdatedAt()
        );
    }

    private EmbeddingSelectionSpec readSelection(String json) {
        try {
            return objectMapper.readValue(json, EmbeddingSelectionSpec.class);
        } catch (JsonProcessingException e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR,
                    "Cannot parse stored cluster set selection", e);
        }
    }

    private String writeJson(Object value) {
        try {
            return objectMapper.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR,
                    "Cannot serialize cluster set selection", e);
        }
    }
}
