package at.procon.dip.ingestion.controller;

import at.procon.dip.domain.access.DocumentAccessContext;
import at.procon.dip.domain.access.DocumentVisibility;
import at.procon.dip.domain.document.CanonicalDocumentMetadata;
import at.procon.dip.domain.document.SourceType;
import at.procon.dip.domain.document.service.DocumentAttributeService;
import at.procon.dip.domain.tenant.TenantRef;
import at.procon.dip.ingestion.config.DipIngestionProperties;
import at.procon.dip.ingestion.dto.GenericImportResponse;
import at.procon.dip.ingestion.dto.GenericIngestionHintsRequest;
import at.procon.dip.ingestion.dto.GenericTextImportRequest;
import at.procon.dip.ingestion.service.DocumentIngestionGateway;
import at.procon.dip.ingestion.spi.IngestionResult;
import at.procon.dip.ingestion.spi.OriginalContentStoragePolicy;
import at.procon.dip.ingestion.spi.SourceDescriptor;
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
import at.procon.dip.runtime.config.RuntimeMode;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
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.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/v1/dip/import")
@ConditionalOnRuntimeMode(RuntimeMode.NEW)
@RequiredArgsConstructor
public class GenericDocumentImportController {

    private final DipIngestionProperties properties;
    private final DocumentIngestionGateway ingestionGateway;
    private final DocumentAttributeService documentAttributeService;

    @PostMapping(path = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<GenericImportResponse> upload(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "ownerTenantKey", required = false) String ownerTenantKey,
            @RequestParam(value = "visibility", required = false) DocumentVisibility visibility,
            @RequestParam(value = "languageCode", required = false) String languageCode,
            @RequestParam(value = "title", required = false) String title,
            @RequestParam(value = "sourceIdentifier", required = false) String sourceIdentifier
    ) throws Exception {
        ensureRestUploadEnabled();

        Map<String, String> attributes = new LinkedHashMap<>();
        if (StringUtils.hasText(languageCode)) {
            attributes.put("languageCode", languageCode);
        }
        if (StringUtils.hasText(title)) {
            attributes.put("title", title);
        }

        SourceDescriptor descriptor = new SourceDescriptor(
                buildAccessContext(ownerTenantKey, visibility),
                SourceType.REST_UPLOAD,
                StringUtils.hasText(sourceIdentifier) ? sourceIdentifier : file.getOriginalFilename(),
                null,
                file.getOriginalFilename(),
                file.getContentType(),
                file.getBytes(),
                null,
                OffsetDateTime.now(),
                OriginalContentStoragePolicy.DEFAULT,
                attributes
        );
        IngestionResult result = ingestionGateway.ingest(descriptor);
        return ResponseEntity.ok(toResponse(result));
    }

    @PostMapping(path = "/text", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<GenericImportResponse> importText(@RequestBody GenericTextImportRequest request) {
        ensureRestUploadEnabled();
        Map<String, String> attributes = buildDescriptorAttributes(request);

        SourceDescriptor descriptor = new SourceDescriptor(
                buildAccessContext(request.ownerTenantKey(), request.visibility()),
                SourceType.API,
                StringUtils.hasText(request.sourceIdentifier()) ? request.sourceIdentifier() : request.fileName(),
                null,
                request.fileName(),
                request.mediaType(),
                request.text() == null ? null : request.text().getBytes(StandardCharsets.UTF_8),
                request.text(),
                OffsetDateTime.now(),
                OriginalContentStoragePolicy.DEFAULT,
                attributes
        );
        IngestionResult result = ingestionGateway.ingest(descriptor);
        GenericImportResponse response = toResponse(result);
        if (response.documentId() != null) {
            documentAttributeService.addAttributes(response.documentId(), request.attributes());
        }
        return ResponseEntity.ok(response);
    }

    private Map<String, String> buildDescriptorAttributes(GenericTextImportRequest request) {
        Map<String, String> attributes = new LinkedHashMap<>();
        if (StringUtils.hasText(request.languageCode())) {
            attributes.put("languageCode", request.languageCode());
        }
        if (StringUtils.hasText(request.title())) {
            attributes.put("title", request.title());
        }
        GenericIngestionHintsRequest hints = request.ingestionHints();
        if (hints != null) {
            putIfHasText(attributes, "documentTypeHint", hints.documentTypeHint());
            putIfHasText(attributes, "embeddingPolicyKey", hints.embeddingPolicyKey());
            putIfHasText(attributes, "embeddingPolicyHint", hints.embeddingPolicyHint());
            putIfHasText(attributes, "importBatchId", hints.importBatchId());
            putIfNotNull(attributes, "wrapperDocument", hints.wrapperDocument());
            putIfNotNull(attributes, "containerDocument", hints.containerDocument());
        }
        return attributes;
    }

    private void putIfHasText(Map<String, String> attributes, String key, String value) {
        if (StringUtils.hasText(value)) {
            attributes.put(key, value.trim());
        }
    }

    private void putIfNotNull(Map<String, String> attributes, String key, Boolean value) {
        if (value != null) {
            attributes.put(key, value.toString());
        }
    }

    private void ensureRestUploadEnabled() {
        if (!properties.isEnabled() || !properties.isRestUploadEnabled()) {
            throw new IllegalStateException("Generic REST import is disabled");
        }
    }

    private DocumentAccessContext buildAccessContext(String ownerTenantKey, DocumentVisibility visibility) {
        DocumentVisibility effectiveVisibility = visibility != null
                ? visibility
                : properties.getDefaultVisibility();
        if (!StringUtils.hasText(ownerTenantKey)) {
            return new DocumentAccessContext(null, effectiveVisibility);
        }
        return new DocumentAccessContext(new TenantRef(null, ownerTenantKey, ownerTenantKey), effectiveVisibility);
    }

    private GenericImportResponse toResponse(IngestionResult result) {
        CanonicalDocumentMetadata metadata = result.documents().stream()
                .findFirst()
                .orElseThrow(() -> new IllegalStateException("No imported document metadata returned"));
        return new GenericImportResponse(
                metadata.documentId(),
                metadata.title(),
                metadata.documentType(),
                metadata.documentFamily(),
                metadata.status(),
                result.warnings().stream().anyMatch(w -> w != null && w.contains("already imported")),
                result.warnings()
        );
    }
}
