package at.procon.dip.clustering.web;

import at.procon.dip.clustering.ClusterRunStatus;
import at.procon.dip.clustering.ClusteringAlgorithm;
import at.procon.dip.clustering.ClusteringExecutionBackend;
import at.procon.dip.clustering.dto.ClusterAssignmentViewResponse;
import at.procon.dip.clustering.dto.ClusterMembersResponse;
import at.procon.dip.clustering.dto.ClusterResponse;
import at.procon.dip.clustering.dto.ClusterRunResponse;
import at.procon.dip.clustering.dto.ClusterSetResponse;
import at.procon.dip.clustering.dto.CreateClusterRunRequest;
import at.procon.dip.clustering.dto.CreateClusterSetRequest;
import at.procon.dip.clustering.dto.EmbeddingSelectionSpec;
import at.procon.dip.clustering.dto.SelectionCountResponse;
import at.procon.dip.clustering.dto.UpdateClusterSetRequest;
import at.procon.dip.clustering.service.EmbeddingClusterAsyncExecutionService;
import at.procon.dip.clustering.service.EmbeddingClusterRunService;
import at.procon.dip.clustering.service.EmbeddingClusterSetService;
import at.procon.dip.clustering.service.EmbeddingSelectionService;
import at.procon.dip.domain.document.DocumentType;
import jakarta.validation.Valid;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/dip/clustering")
@RequiredArgsConstructor
public class EmbeddingClusterController {

    private final EmbeddingSelectionService selectionService;
    private final EmbeddingClusterSetService clusterSetService;
    private final EmbeddingClusterRunService runService;
    private final EmbeddingClusterAsyncExecutionService asyncExecutionService;

    @PostMapping("/selection/count")
    public ResponseEntity<SelectionCountResponse> countSelection(@RequestBody EmbeddingSelectionSpec selection) {
        return ResponseEntity.ok(new SelectionCountResponse(selectionService.count(selection)));
    }

    @PostMapping("/sets")
    public ResponseEntity<ClusterSetResponse> createSet(@Valid @RequestBody CreateClusterSetRequest request) {
        return ResponseEntity.ok(clusterSetService.create(request));
    }

    @PutMapping("/sets/{id}")
    public ResponseEntity<ClusterSetResponse> updateSet(@PathVariable UUID id,
                                                        @Valid @RequestBody UpdateClusterSetRequest request) {
        return ResponseEntity.ok(clusterSetService.update(id, request));
    }

    @GetMapping("/sets")
    public ResponseEntity<List<ClusterSetResponse>> listSets(
            @RequestParam(name = "active", required = false) Boolean active) {
        return ResponseEntity.ok(clusterSetService.list(active));
    }

    @GetMapping("/sets/{id}")
    public ResponseEntity<ClusterSetResponse> getSet(@PathVariable UUID id) {
        return ResponseEntity.ok(clusterSetService.get(id));
    }

    @PostMapping("/runs")
    public ResponseEntity<ClusterRunResponse> createRun(@Valid @RequestBody CreateClusterRunRequest request) {
        return ResponseEntity.ok(runService.createRun(request));
    }

    @GetMapping("/runs")
    public ResponseEntity<List<ClusterRunResponse>> listRuns(
            @RequestParam(name = "status", required = false) ClusterRunStatus status,
            @RequestParam(name = "algorithm", required = false) ClusteringAlgorithm algorithm,
            @RequestParam(name = "executionBackend", required = false) ClusteringExecutionBackend executionBackend,
            @RequestParam(name = "documentType", required = false) DocumentType documentType,
            @RequestParam(name = "nameLike", required = false) String nameLike,
            @RequestParam(name = "createdFrom", required = false)
            @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime createdFrom,
            @RequestParam(name = "createdTo", required = false)
            @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime createdTo) {
        return ResponseEntity.ok(runService.listRuns(
                status, algorithm, executionBackend, documentType, nameLike, createdFrom, createdTo));
    }

    @PostMapping("/runs/{id}/start")
    public ResponseEntity<ClusterRunResponse> startRun(@PathVariable UUID id) {
        ClusterRunResponse queued = runService.queueRun(id);
        asyncExecutionService.executeRunAsync(id);
        return ResponseEntity.accepted().body(queued);
    }

    @PostMapping("/runs/{id}/cancel")
    public ResponseEntity<ClusterRunResponse> cancelRun(@PathVariable UUID id) {
        return ResponseEntity.ok(runService.requestCancellation(id));
    }

    @GetMapping("/runs/{id}")
    public ResponseEntity<ClusterRunResponse> getRun(@PathVariable UUID id) {
        return ResponseEntity.ok(runService.getRun(id));
    }

    @GetMapping("/runs/{id}/clusters")
    public ResponseEntity<List<ClusterResponse>> listClusters(@PathVariable UUID id) {
        return ResponseEntity.ok(runService.listClusters(id));
    }

    @GetMapping("/runs/{id}/assignments")
    public ResponseEntity<List<ClusterAssignmentViewResponse>> listAssignments(
            @PathVariable UUID id,
            @RequestParam(name = "includeText", defaultValue = "false") boolean includeText) {
        return ResponseEntity.ok(runService.listAssignments(id, includeText));
    }

    @GetMapping("/runs/{runId}/clusters/{clusterId}/members")
    public ResponseEntity<List<ClusterMembersResponse>> listClusterMembers(
            @PathVariable UUID runId,
            @PathVariable UUID clusterId,
            @RequestParam(name = "includeText", defaultValue = "false") boolean includeText) {
        return ResponseEntity.ok(runService.listClusterMembers(runId, clusterId, includeText));
    }
}
