package de.assecutor.pdftool.api;

import com.fasterxml.jackson.databind.ObjectMapper;
import de.assecutor.pdftool.invoice.InvoiceMetadata;
import de.assecutor.pdftool.zugferd.ZugferdConversionException;
import de.assecutor.pdftool.zugferd.ZugferdResult;
import de.assecutor.pdftool.zugferd.ZugferdService;
import de.assecutor.pdftool.zugferd.ZugferdValidationService;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validator;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * REST-API zur Erzeugung und Prüfung rechtskonformer ZUGFeRD-E-Rechnungen.
 *
 * <pre>
 * curl -X POST http://localhost:8080/api/invoices/sign \
 *   -F "file=@rechnung.pdf" \
 *   -F "metadata=@metadata.json;type=application/json" \
 *   -o rechnung-zugferd.zip
 *
 * curl -X POST http://localhost:8080/api/invoices/validate \
 *   -F "file=@rechnung-zugferd.pdf"
 * </pre>
 */
@RestController
@RequestMapping("/api/invoices")
public class InvoiceSigningController {

    /** Response-Header mit dem Ergebnis der Mustang-Validierung (true/false). */
    public static final String VALIDATION_HEADER = "X-Zugferd-Valid";

    private final ZugferdService zugferdService;
    private final ZugferdValidationService validationService;
    private final ObjectMapper objectMapper;
    private final Validator validator;

    public InvoiceSigningController(ZugferdService zugferdService,
                                    ZugferdValidationService validationService,
                                    ObjectMapper objectMapper,
                                    Validator validator) {
        this.zugferdService = zugferdService;
        this.validationService = validationService;
        this.objectMapper = objectMapper;
        this.validator = validator;
    }

    @PostMapping(value = "/sign", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<byte[]> sign(@RequestPart("file") MultipartFile file,
                                       @RequestPart("metadata") String metadataJson) throws IOException {
        InvoiceMetadata metadata = parseAndValidate(metadataJson);
        ZugferdResult result = zugferdService.createZugferdZip(file.getBytes(), metadata);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.parseMediaType("application/zip"));
        headers.setContentDisposition(ContentDisposition.attachment().filename(result.zipFileName()).build());
        headers.set(VALIDATION_HEADER, String.valueOf(result.valid()));
        return new ResponseEntity<>(result.zip(), headers, HttpStatus.OK);
    }

    /**
     * Validiert eine bestehende ZUGFeRD-Rechnung (PDF oder Factur-X-XML).
     * Antwort ist der Mustang-Prüfbericht als XML; HTTP 200 bei gültiger,
     * HTTP 422 bei ungültiger Rechnung.
     */
    @PostMapping(value = "/validate", consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
            produces = MediaType.APPLICATION_XML_VALUE)
    public ResponseEntity<String> validate(@RequestPart("file") MultipartFile file) throws IOException {
        ZugferdValidationService.ValidationResult result =
                validationService.validate(file.getBytes(), file.getOriginalFilename());

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_XML);
        headers.set(VALIDATION_HEADER, String.valueOf(result.valid()));
        return new ResponseEntity<>(result.reportXml(), headers,
                result.valid() ? HttpStatus.OK : HttpStatus.UNPROCESSABLE_ENTITY);
    }

    private InvoiceMetadata parseAndValidate(String metadataJson) {
        InvoiceMetadata metadata;
        try {
            metadata = objectMapper.readValue(metadataJson, InvoiceMetadata.class);
        } catch (IOException e) {
            throw new ZugferdConversionException("Metadaten sind kein gültiges JSON: " + e.getMessage(), e);
        }
        Set<ConstraintViolation<InvoiceMetadata>> violations = validator.validate(metadata);
        if (!violations.isEmpty()) {
            String details = violations.stream()
                    .map(v -> v.getPropertyPath() + ": " + v.getMessage())
                    .sorted()
                    .collect(Collectors.joining("; "));
            throw new ZugferdConversionException("Metadaten unvollständig: " + details);
        }
        return metadata;
    }

    @ExceptionHandler(ZugferdConversionException.class)
    public ResponseEntity<Map<String, String>> handleConversionError(ZugferdConversionException e) {
        return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
    }
}
