package at.procon.dip.classification.detector;

import at.procon.dip.classification.spi.DetectionResult;
import at.procon.dip.classification.spi.DocumentTypeDetector;
import at.procon.dip.domain.document.DocumentFamily;
import at.procon.dip.domain.document.DocumentType;
import at.procon.dip.ingestion.spi.SourceDescriptor;
import at.procon.dip.ingestion.util.DocumentImportSupport;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * Basic Phase 4 detector using file extension, media type, and lightweight heuristics.
 */
@Component
public class BasicMimeAndExtensionDocumentTypeDetector implements DocumentTypeDetector {

    @Override
    public boolean supports(SourceDescriptor sourceDescriptor) {
        return true;
    }

    @Override
    public DetectionResult detect(SourceDescriptor sourceDescriptor) {
        String normalizedMediaType = DocumentImportSupport.inferKnownMimeType(sourceDescriptor.fileName(), sourceDescriptor.mediaType());
        String extension = DocumentImportSupport.extensionOf(sourceDescriptor.fileName());
        if (sourceDescriptor.attributes() != null && StringUtils.hasText(sourceDescriptor.attributes().get("documentTypeHint"))) {
            try {
                DocumentType hintedType = DocumentType.valueOf(sourceDescriptor.attributes().get("documentTypeHint").trim());
                return new DetectionResult(hintedType, DocumentImportSupport.familyFor(hintedType),
                        normalizedMediaType,
                        sourceDescriptor.attributes().get("languageCode"),
                        Map.of("documentTypeHint", hintedType.name()));
            } catch (IllegalArgumentException ignored) {
                // fall back to normal detection
            }
        }

        if (sourceDescriptor.sourceType() == at.procon.dip.domain.document.SourceType.TED_PACKAGE) {
            Map<String, String> attributes = new HashMap<>();
            attributes.put("sourceType", sourceDescriptor.sourceType().name());
            if (StringUtils.hasText(sourceDescriptor.fileName())) {
                attributes.put("fileName", sourceDescriptor.fileName());
            }
            return new DetectionResult(DocumentType.TED_PACKAGE, DocumentFamily.PROCUREMENT,
                    normalizedMediaType != null ? normalizedMediaType : "application/gzip", null, attributes);
        }

        DocumentType documentType = detectByMediaType(normalizedMediaType);
        if (documentType == DocumentType.UNKNOWN) {
            documentType = detectByExtension(extension);
        }
        if (documentType == DocumentType.UNKNOWN && sourceDescriptor.hasInlineTextContent()) {
            documentType = DocumentType.TEXT;
        }

        DocumentFamily family = DocumentImportSupport.familyFor(documentType);
        String languageCode = sourceDescriptor.attributes() == null ? null : sourceDescriptor.attributes().get("languageCode");
        Map<String, String> attributes = new HashMap<>();
        attributes.put("detectedExtension", extension);
        if (normalizedMediaType != null) {
            attributes.put("normalizedMediaType", normalizedMediaType);
        }
        if (StringUtils.hasText(sourceDescriptor.fileName())) {
            attributes.put("fileName", sourceDescriptor.fileName());
        }

        return new DetectionResult(documentType, family, normalizedMediaType, languageCode, attributes);
    }

    private DocumentType detectByMediaType(String mediaType) {
        if (!StringUtils.hasText(mediaType)) {
            return DocumentType.UNKNOWN;
        }
        return switch (mediaType.toLowerCase(Locale.ROOT)) {
            case "application/pdf", "application/x-pdf" -> DocumentType.PDF;
            case "text/html", "application/xhtml+xml" -> DocumentType.HTML;
            case "text/plain", "text/csv", "text/tab-separated-values", "application/csv", "application/x-csv" -> DocumentType.TEXT;
            case "text/markdown", "text/x-markdown" -> DocumentType.MARKDOWN;
            case "application/xml", "text/xml", "application/json", "application/yaml" -> DocumentType.XML_GENERIC;
            case "message/rfc822" -> DocumentType.MIME_MESSAGE;
            case "application/zip", "application/x-zip-compressed" -> DocumentType.ZIP_ARCHIVE;
            case "application/vnd.ms-excel",
                    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
                    "application/vnd.ms-excel.sheet.macroenabled.12",
                    "application/vnd.ms-excel.sheet.binary.macroenabled.12",
                    "application/vnd.oasis.opendocument.spreadsheet",
                    "application/msword",
                    "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                    "application/vnd.ms-powerpoint",
                    "application/vnd.openxmlformats-officedocument.presentationml.presentation",
                    "application/vnd.oasis.opendocument.text",
                    "application/vnd.oasis.opendocument.presentation",
                    "application/octet-stream",
                    "binary/octet-stream" -> DocumentType.GENERIC_BINARY;
            default -> mediaType.startsWith("text/") ? DocumentType.TEXT : DocumentType.UNKNOWN;
        };
    }

    private DocumentType detectByExtension(String extension) {
        return switch (extension) {
            case "pdf" -> DocumentType.PDF;
            case "html", "htm" -> DocumentType.HTML;
            case "txt", "log", "csv", "tsv", "json", "yaml", "yml" -> DocumentType.TEXT;
            case "md", "markdown" -> DocumentType.MARKDOWN;
            case "xml", "xsd", "xslt" -> DocumentType.XML_GENERIC;
            case "eml", "msg" -> DocumentType.MIME_MESSAGE;
            case "zip" -> DocumentType.ZIP_ARCHIVE;
            case "docx" -> DocumentType.DOCX;
            case "xls", "xlsx", "xlsm", "xlsb", "ods", "doc", "ppt", "pptx", "odt", "odp" -> DocumentType.GENERIC_BINARY;
            default -> DocumentType.UNKNOWN;
        };
    }
}
