ReportApiController.java

package com.taxonomy.architecture.controller;

import com.taxonomy.architecture.report.ReportRendererRegistry;
import com.taxonomy.architecture.service.ArchitectureReportService;
import com.taxonomy.dto.ArchitectureReport;
import com.taxonomy.extension.api.report.ReportFormatDescriptor;
import com.taxonomy.extension.api.report.ReportRenderContext;
import com.taxonomy.extension.api.report.ReportRenderResult;
import com.taxonomy.extension.api.report.ReportRendererExtension;
import com.taxonomy.versioning.service.RepositoryStateService;
import com.taxonomy.workspace.service.WorkspaceContext;
import com.taxonomy.workspace.service.WorkspaceResolver;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.RestController;

import java.util.Map;

@RestController
@RequestMapping("/api/report")
@Tag(name = "Report Export")
public class ReportApiController {

    private final ArchitectureReportService reportService;
    private final ReportRendererRegistry reportRendererRegistry;
    private final RepositoryStateService repositoryStateService;
    private final WorkspaceResolver workspaceResolver;

    public ReportApiController(ArchitectureReportService reportService,
                               ReportRendererRegistry reportRendererRegistry,
                               RepositoryStateService repositoryStateService,
                               WorkspaceResolver workspaceResolver) {
        this.reportService = reportService;
        this.reportRendererRegistry = reportRendererRegistry;
        this.repositoryStateService = repositoryStateService;
        this.workspaceResolver = workspaceResolver;
    }

    public record ReportRequest(
            Map<String, Integer> scores,
            String businessText,
            int minScore) {
    }

    @Operation(summary = "Export Markdown report",
            description = "Generates a comprehensive architecture report as Markdown")
    @ApiResponse(responseCode = "200", description = "Markdown report returned")
    @ApiResponse(responseCode = "400", description = "Scores are missing")
    @PostMapping("/markdown")
    public ResponseEntity<byte[]> exportMarkdown(@RequestBody ReportRequest request) {
        return render(request, "markdown");
    }

    @Operation(summary = "Export HTML report",
            description = "Generates a comprehensive architecture report as standalone HTML")
    @ApiResponse(responseCode = "200", description = "HTML report returned")
    @ApiResponse(responseCode = "400", description = "Scores are missing")
    @PostMapping("/html")
    public ResponseEntity<byte[]> exportHtml(@RequestBody ReportRequest request) {
        return render(request, "html");
    }

    @Operation(summary = "Export DOCX report",
            description = "Generates a comprehensive architecture report as Word document")
    @ApiResponse(responseCode = "200", description = "DOCX report returned as binary")
    @ApiResponse(responseCode = "400", description = "Scores are missing")
    @PostMapping("/docx")
    public ResponseEntity<byte[]> exportDocx(@RequestBody ReportRequest request) {
        return render(request, "docx");
    }

    @Operation(summary = "Export JSON report",
            description = "Returns the full architecture report as structured JSON")
    @ApiResponse(responseCode = "200", description = "JSON report returned")
    @ApiResponse(responseCode = "400", description = "Scores are missing")
    @PostMapping("/json")
    public ResponseEntity<ArchitectureReport> exportJson(@RequestBody ReportRequest request) {
        if (!isValid(request)) {
            return ResponseEntity.badRequest().build();
        }
        ArchitectureReport report = generateReport(request);
        WorkspaceContext context = workspaceResolver.resolveCurrentContext();
        String username = context.username();
        String branch = repositoryStateService.resolveWorkspaceBranch(username);
        report.setViewContext(repositoryStateService.getViewContext(username, branch, context));
        return ResponseEntity.ok(report);
    }

    private ResponseEntity<byte[]> render(ReportRequest request, String formatId) {
        if (!isValid(request)) {
            return ResponseEntity.badRequest().build();
        }
        ArchitectureReport report = generateReport(request);
        ReportRendererExtension renderer = reportRendererRegistry.getRequired(formatId);
        ReportFormatDescriptor format = renderer.descriptor();
        ReportRenderResult rendered = renderer.render(ReportRenderContext.of(report));
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename=\"architecture-report."
                                + format.fileExtension() + "\"")
                .contentType(MediaType.parseMediaType(format.contentType()))
                .body(rendered.bytes());
    }

    private ArchitectureReport generateReport(ReportRequest request) {
        return reportService.generateReport(
                request.scores(), request.businessText(), request.minScore());
    }

    private boolean isValid(ReportRequest request) {
        return request != null && request.scores() != null && !request.scores().isEmpty();
    }
}