package com.ai.trackwalker.api;

import com.ai.trackwalker.api.dto.ModelManagementRequests;
import com.ai.trackwalker.model.ModelManagementService;
import com.ai.trackwalker.model.ModelTestEventPublisher;
import jakarta.validation.Valid;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/v1")
public class ModelManagementController {
    private final ModelManagementService service;
    private final ModelTestEventPublisher events;

    public ModelManagementController(ModelManagementService service, ModelTestEventPublisher events) {
        this.service = service;
        this.events = events;
    }

    @GetMapping("/models")
    public ApiResponse<?> models(
            @RequestParam(required = false) String keyword,
            @RequestParam(required = false) String modelType,
            @RequestParam(required = false) String status
    ) {
        return ApiResponse.ok(Map.of("models", service.listModels(keyword, modelType, status)));
    }

    @GetMapping("/models/{modelGroupId}")
    public ApiResponse<?> model(@PathVariable String modelGroupId) {
        return ApiResponse.ok(service.modelDetail(modelGroupId));
    }

    @GetMapping("/models/{modelGroupId}/versions")
    public ApiResponse<?> versions(@PathVariable String modelGroupId) {
        return ApiResponse.ok(Map.of("versions", service.listVersions(modelGroupId)));
    }

    @GetMapping("/model-versions/{versionId}/artifacts")
    public ApiResponse<?> artifacts(@PathVariable String versionId) {
        return ApiResponse.ok(Map.of("artifacts", service.listArtifacts(versionId)));
    }

    @PostMapping("/model-versions/{versionId}/validate")
    public ApiResponse<?> validateArtifact(
            @PathVariable String versionId,
            @RequestBody(required = false) Map<String, Object> request
    ) {
        return ApiResponse.ok(service.validateArtifact(versionId, operator(request, "算法工程师")));
    }

    @GetMapping("/model-versions/{versionId}/parameter-schema")
    public ApiResponse<?> parameterSchema(@PathVariable String versionId) {
        return ApiResponse.ok(service.parameterSchema(versionId));
    }

    @GetMapping("/models/{modelGroupId}/parameter-profiles")
    public ApiResponse<?> parameterProfiles(@PathVariable String modelGroupId) {
        return ApiResponse.ok(Map.of("profiles", service.listParameterProfiles(modelGroupId)));
    }

    @PostMapping("/models/{modelGroupId}/parameter-profiles")
    public ApiResponse<?> createParameterProfile(
            @PathVariable String modelGroupId,
            @Valid @RequestBody ModelManagementRequests.CreateParameterProfileRequest request
    ) {
        return ApiResponse.ok(service.createParameterProfile(modelGroupId, request));
    }

    @PostMapping("/parameter-profiles/{profileId}/revisions")
    public ApiResponse<?> createParameterRevision(
            @PathVariable String profileId,
            @Valid @RequestBody ModelManagementRequests.CreateParameterRevisionRequest request
    ) {
        return ApiResponse.ok(service.createParameterRevision(profileId, request));
    }

    @PostMapping("/parameter-profiles/validate")
    public ApiResponse<?> validateParameters(@Valid @RequestBody ModelManagementRequests.ValidateParametersRequest request) {
        return ApiResponse.ok(service.validateParameters(request));
    }

    @GetMapping("/parameter-profiles/compare")
    public ApiResponse<?> compareParameterProfiles(@RequestParam List<String> profileIds) {
        return ApiResponse.ok(service.compareParameterProfiles(profileIds));
    }

    @PostMapping("/parameter-profiles/{profileId}/activate")
    public ApiResponse<?> activateProfile(
            @PathVariable String profileId,
            @RequestBody(required = false) Map<String, Object> request
    ) {
        return ApiResponse.ok(service.activateParameterProfile(profileId, operator(request, "算法工程师")));
    }

    @PostMapping("/parameter-profiles/{profileId}/deactivate")
    public ApiResponse<?> deactivateProfile(
            @PathVariable String profileId,
            @RequestBody(required = false) Map<String, Object> request
    ) {
        return ApiResponse.ok(service.deactivateParameterProfile(profileId, operator(request, "算法工程师")));
    }

    @GetMapping("/model-test-samples")
    public ApiResponse<?> modelTestSamples(
            @RequestParam String modelGroupId,
            @RequestParam(required = false) String keyword,
            @RequestParam(required = false) String modality,
            @RequestParam(required = false) String labelStatus,
            @RequestParam(required = false) String difficulty
    ) {
        return ApiResponse.ok(Map.of("samples", service.listModelSamples(modelGroupId, keyword, modality, labelStatus, difficulty)));
    }

    @GetMapping("/models/{modelGroupId}/sample-sets")
    public ApiResponse<?> sampleSets(@PathVariable String modelGroupId) {
        return ApiResponse.ok(Map.of("sample_sets", service.listSampleSets(modelGroupId)));
    }

    @PostMapping("/sample-sets/preview")
    public ApiResponse<?> previewSampleSet(@Valid @RequestBody ModelManagementRequests.SampleSetPreviewRequest request) {
        return ApiResponse.ok(service.previewSampleSet(request.sampleIds()));
    }

    @PostMapping("/test-uploads")
    public ApiResponse<?> createUploadSession(@Valid @RequestBody ModelManagementRequests.CreateUploadSessionRequest request) {
        return ApiResponse.ok(service.createUploadSession(request));
    }

    @PostMapping(value = "/test-uploads/{sessionId}/files", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ApiResponse<?> uploadFiles(@PathVariable String sessionId, @RequestPart("files") MultipartFile[] files) {
        return ApiResponse.ok(service.uploadFiles(sessionId, files));
    }

    @PostMapping("/test-uploads/{sessionId}/chunked-files")
    public ApiResponse<?> createChunkedFile(
            @PathVariable String sessionId,
            @Valid @RequestBody ModelManagementRequests.CreateChunkedFileRequest request
    ) {
        return ApiResponse.ok(service.createChunkedFile(sessionId, request));
    }

    @PutMapping(value = "/test-uploads/{sessionId}/chunked-files/{manifestId}/parts/{partIndex}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ApiResponse<?> uploadChunk(
            @PathVariable String sessionId,
            @PathVariable String manifestId,
            @PathVariable int partIndex,
            @RequestPart("chunk") MultipartFile chunk
    ) {
        return ApiResponse.ok(service.uploadChunk(sessionId, manifestId, partIndex, chunk));
    }

    @PostMapping("/test-uploads/{sessionId}/chunked-files/{manifestId}/complete")
    public ApiResponse<?> completeChunkedFile(@PathVariable String sessionId, @PathVariable String manifestId) {
        return ApiResponse.ok(service.completeChunkedFile(sessionId, manifestId));
    }

    @PostMapping("/test-uploads/{sessionId}/complete")
    public ApiResponse<?> completeUpload(@PathVariable String sessionId) {
        return ApiResponse.ok(service.completeUploadSession(sessionId));
    }

    @GetMapping("/test-uploads/{sessionId}")
    public ApiResponse<?> uploadSession(@PathVariable String sessionId) {
        return ApiResponse.ok(service.uploadSession(sessionId));
    }

    @DeleteMapping("/test-uploads/{sessionId}")
    public ApiResponse<?> deleteUploadSession(@PathVariable String sessionId) {
        return ApiResponse.ok(service.deleteUploadSession(sessionId));
    }

    @GetMapping("/test-upload-files/{fileId}/content")
    public ResponseEntity<byte[]> uploadedContent(@PathVariable String fileId) {
        ModelManagementService.UploadedContent content = service.uploadedContent(fileId);
        MediaType mediaType;
        try {
            mediaType = MediaType.parseMediaType(content.contentType());
        } catch (Exception ignored) {
            mediaType = MediaType.APPLICATION_OCTET_STREAM;
        }
        return ResponseEntity.ok()
                .contentType(mediaType)
                .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.inline()
                        .filename(content.fileName(), StandardCharsets.UTF_8).build().toString())
                .body(content.content());
    }

    @PostMapping("/model-tests")
    public ApiResponse<?> createTest(@Valid @RequestBody ModelManagementRequests.CreateModelTestRequest request) {
        return ApiResponse.ok(service.createTest(request));
    }

    @GetMapping("/model-tests")
    public ApiResponse<?> tests(
            @RequestParam(required = false) String modelGroupId,
            @RequestParam(required = false) String status,
            @RequestParam(defaultValue = "100") int limit
    ) {
        return ApiResponse.ok(Map.of("tests", service.listTests(modelGroupId, status, limit)));
    }

    @GetMapping("/model-tests/{taskId}")
    public ApiResponse<?> test(@PathVariable String taskId) {
        return ApiResponse.ok(service.testDetail(taskId));
    }

    @PostMapping("/model-tests/{taskId}/validate")
    public ApiResponse<?> validateTest(@PathVariable String taskId) {
        return ApiResponse.ok(service.validateTest(taskId));
    }

    @PostMapping("/model-tests/{taskId}/start")
    public ApiResponse<?> startTest(@PathVariable String taskId) {
        return ApiResponse.ok(service.startTest(taskId));
    }

    @PostMapping("/model-tests/{taskId}/cancel")
    public ApiResponse<?> cancelTest(@PathVariable String taskId) {
        return ApiResponse.ok(service.cancelTest(taskId));
    }

    @PostMapping("/model-tests/{taskId}/clone")
    public ApiResponse<?> cloneTest(
            @PathVariable String taskId,
            @RequestBody(required = false) Map<String, Object> request
    ) {
        return ApiResponse.ok(service.cloneTest(taskId, operator(request, "测试人员")));
    }

    @GetMapping("/model-tests/{taskId}/events")
    public SseEmitter testEvents(
            @PathVariable String taskId,
            @RequestParam(defaultValue = "0") long afterSequence
    ) {
        return events.subscribe(taskId, afterSequence);
    }

    @GetMapping("/model-tests/{taskId}/results")
    public ApiResponse<?> testResults(@PathVariable String taskId) {
        return ApiResponse.ok(Map.of("results", service.testResults(taskId)));
    }

    @GetMapping("/model-tests/{taskId}/summary")
    public ApiResponse<?> testSummary(@PathVariable String taskId) {
        return ApiResponse.ok(Map.of("summaries", service.testSummaries(taskId)));
    }

    @GetMapping("/model-tests/{taskId}/report")
    public ResponseEntity<String> testReport(@PathVariable String taskId) {
        ModelManagementService.TestReport report = service.testReport(taskId);
        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType("text/markdown;charset=UTF-8"))
                .header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment()
                        .filename(report.fileName(), StandardCharsets.UTF_8).build().toString())
                .body(report.content());
    }

    @GetMapping("/model-tests/compare")
    public ApiResponse<?> compareTests(@RequestParam List<String> taskIds) {
        return ApiResponse.ok(service.compareTests(taskIds));
    }

    @PostMapping("/model-test-results/{resultId}/feedback")
    public ApiResponse<?> resultFeedback(
            @PathVariable String resultId,
            @Valid @RequestBody ModelManagementRequests.ResultFeedbackRequest request
    ) {
        return ApiResponse.ok(service.addResultFeedback(resultId, request));
    }

    @GetMapping("/model-releases")
    public ApiResponse<?> releases(@RequestParam(required = false) String modelGroupId) {
        return ApiResponse.ok(Map.of("releases", service.listReleases(modelGroupId)));
    }

    @PostMapping("/model-releases")
    public ApiResponse<?> createRelease(@Valid @RequestBody ModelManagementRequests.CreateReleaseRequest request) {
        return ApiResponse.ok(service.createRelease(request));
    }

    @PostMapping("/model-releases/{releaseId}/approve")
    public ApiResponse<?> approveRelease(
            @PathVariable String releaseId,
            @RequestBody ModelManagementRequests.ReleaseDecisionRequest request
    ) {
        return ApiResponse.ok(service.approveRelease(releaseId, request));
    }

    @PostMapping("/model-releases/{releaseId}/reject")
    public ApiResponse<?> rejectRelease(
            @PathVariable String releaseId,
            @RequestBody ModelManagementRequests.ReleaseDecisionRequest request
    ) {
        return ApiResponse.ok(service.rejectRelease(releaseId, request));
    }

    @PostMapping("/model-releases/{releaseId}/gray")
    public ApiResponse<?> grayRelease(
            @PathVariable String releaseId,
            @RequestBody ModelManagementRequests.GrayReleaseRequest request
    ) {
        return ApiResponse.ok(service.grayRelease(releaseId, request));
    }

    @PostMapping("/model-releases/{releaseId}/promote")
    public ApiResponse<?> promoteRelease(
            @PathVariable String releaseId,
            @RequestBody ModelManagementRequests.ReleaseDecisionRequest request
    ) {
        return ApiResponse.ok(service.promoteRelease(releaseId, request));
    }

    @PostMapping("/model-releases/{releaseId}/rollback")
    public ApiResponse<?> rollbackRelease(
            @PathVariable String releaseId,
            @Valid @RequestBody ModelManagementRequests.RollbackReleaseRequest request
    ) {
        return ApiResponse.ok(service.rollbackRelease(releaseId, request));
    }

    @GetMapping("/model-audit-logs")
    public ApiResponse<?> auditLogs(
            @RequestParam(required = false) String entityType,
            @RequestParam(required = false) String entityId
    ) {
        return ApiResponse.ok(Map.of("logs", service.auditLogs(entityType, entityId)));
    }

    private String operator(Map<String, Object> request, String fallback) {
        if (request == null || request.get("operator") == null || String.valueOf(request.get("operator")).isBlank()) {
            return fallback;
        }
        return String.valueOf(request.get("operator"));
    }
}
