package com.datamate.datamanagement.application;

import com.datamate.datamanagement.common.enums.KnowledgeItemPreviewStatus;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem;
import com.datamate.datamanagement.infrastructure.config.DataManagementProperties;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Set;

/**
 * 知识条目预览转换异步任务
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class KnowledgeItemPreviewAsyncService {
    private static final Set<String> OFFICE_EXTENSIONS = Set.of("doc", "docx");
    private static final String KNOWLEDGE_ITEM_UPLOAD_DIR = "knowledge-items";
    private static final String PREVIEW_SUB_DIR = "preview";
    private static final String PREVIEW_FILE_SUFFIX = ".pdf";
    private static final String PATH_SEPARATOR = "/";
    private static final int MAX_ERROR_LENGTH = 500;
    private static final DateTimeFormatter PREVIEW_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

    private final KnowledgeItemRepository knowledgeItemRepository;
    private final DataManagementProperties dataManagementProperties;
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Async
    public void convertPreviewAsync(String itemId) {
        if (StringUtils.isBlank(itemId)) {
            return;
        }
        KnowledgeItem item = knowledgeItemRepository.getById(itemId);
        if (item == null) {
            return;
        }
        String extension = resolveFileExtension(resolveOriginalName(item));
        if (!OFFICE_EXTENSIONS.contains(extension)) {
            updatePreviewStatus(item, KnowledgeItemPreviewStatus.FAILED, null, "仅支持 DOC/DOCX 转换");
            return;
        }
        if (StringUtils.isBlank(item.getContent())) {
            updatePreviewStatus(item, KnowledgeItemPreviewStatus.FAILED, null, "源文件路径为空");
            return;
        }
        Path sourcePath = resolveKnowledgeItemStoragePath(item.getContent());
        if (!Files.exists(sourcePath) || !Files.isRegularFile(sourcePath)) {
            updatePreviewStatus(item, KnowledgeItemPreviewStatus.FAILED, null, "源文件不存在");
            return;
        }

        KnowledgeItemPreviewMetadataHelper.PreviewInfo previewInfo = KnowledgeItemPreviewMetadataHelper
            .readPreviewInfo(item.getMetadata(), objectMapper);
        String previewRelativePath = StringUtils.defaultIfBlank(
            previewInfo.pdfPath(),
            resolvePreviewRelativePath(item.getSetId(), item.getId())
        );
        Path targetPath = resolvePreviewStoragePath(previewRelativePath);
        ensureParentDirectory(targetPath);

        try {
            LibreOfficeConverter.convertToPdf(sourcePath, targetPath);
            updatePreviewStatus(item, KnowledgeItemPreviewStatus.READY, previewRelativePath, null);
        } catch (Exception e) {
            log.error("preview convert failed, itemId: {}", item.getId(), e);
            updatePreviewStatus(item, KnowledgeItemPreviewStatus.FAILED, previewRelativePath, trimError(e.getMessage()));
        }
    }

    private void updatePreviewStatus(
        KnowledgeItem item,
        KnowledgeItemPreviewStatus status,
        String previewRelativePath,
        String error
    ) {
        if (item == null) {
            return;
        }
        String updatedMetadata = KnowledgeItemPreviewMetadataHelper.applyPreviewInfo(
            item.getMetadata(),
            objectMapper,
            status,
            previewRelativePath,
            error,
            nowText()
        );
        item.setMetadata(updatedMetadata);
        knowledgeItemRepository.updateById(item);
    }

    private String resolveOriginalName(KnowledgeItem item) {
        if (item == null) {
            return "";
        }
        if (StringUtils.isNotBlank(item.getSourceFileId())) {
            return item.getSourceFileId();
        }
        if (StringUtils.isNotBlank(item.getContent())) {
            return Paths.get(item.getContent()).getFileName().toString();
        }
        return "";
    }

    private String resolveFileExtension(String fileName) {
        if (StringUtils.isBlank(fileName)) {
            return "";
        }
        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex <= 0 || dotIndex >= fileName.length() - 1) {
            return "";
        }
        return fileName.substring(dotIndex + 1).toLowerCase();
    }

    private String resolvePreviewRelativePath(String setId, String itemId) {
        String relativePath = Paths.get(KNOWLEDGE_ITEM_UPLOAD_DIR, setId, PREVIEW_SUB_DIR, itemId + PREVIEW_FILE_SUFFIX)
            .toString();
        return relativePath.replace("\\", PATH_SEPARATOR);
    }

    private Path resolvePreviewStoragePath(String relativePath) {
        String normalizedRelativePath = StringUtils.defaultString(relativePath).replace("/", java.io.File.separator);
        Path root = resolveUploadRootPath();
        Path target = root.resolve(normalizedRelativePath).toAbsolutePath().normalize();
        if (!target.startsWith(root)) {
            throw new IllegalArgumentException("invalid preview path");
        }
        return target;
    }

    private Path resolveKnowledgeItemStoragePath(String relativePath) {
        if (StringUtils.isBlank(relativePath)) {
            throw new IllegalArgumentException("invalid knowledge item path");
        }
        String normalizedInput = relativePath.replace("\\", PATH_SEPARATOR).trim();
        Path root = resolveUploadRootPath();
        java.util.LinkedHashSet<Path> candidates = new java.util.LinkedHashSet<>();

        Path inputPath = Paths.get(normalizedInput.replace(PATH_SEPARATOR, java.io.File.separator));
        if (inputPath.isAbsolute()) {
            Path normalizedAbsolute = inputPath.toAbsolutePath().normalize();
            if (normalizedAbsolute.startsWith(root)) {
                candidates.add(normalizedAbsolute);
            }
            String segmentRelativePath = extractRelativePathFromSegment(normalizedInput, KNOWLEDGE_ITEM_UPLOAD_DIR);
            if (StringUtils.isNotBlank(segmentRelativePath)) {
                candidates.add(buildKnowledgeItemStoragePath(root, segmentRelativePath));
            }
            if (candidates.isEmpty()) {
                throw new IllegalArgumentException("invalid knowledge item path");
            }
        } else {
            String normalizedRelative = normalizeRelativePathValue(normalizedInput);
            if (StringUtils.isNotBlank(normalizedRelative)) {
                candidates.add(buildKnowledgeItemStoragePath(root, normalizedRelative));
            }
            String segmentRelativePath = extractRelativePathFromSegment(normalizedInput, KNOWLEDGE_ITEM_UPLOAD_DIR);
            if (StringUtils.isNotBlank(segmentRelativePath)) {
                candidates.add(buildKnowledgeItemStoragePath(root, segmentRelativePath));
            }
            if (StringUtils.isNotBlank(normalizedRelative)
                    && !normalizedRelative.startsWith(KNOWLEDGE_ITEM_UPLOAD_DIR + PATH_SEPARATOR)
                    && !normalizedRelative.equals(KNOWLEDGE_ITEM_UPLOAD_DIR)) {
                candidates.add(buildKnowledgeItemStoragePath(root, KNOWLEDGE_ITEM_UPLOAD_DIR + PATH_SEPARATOR + normalizedRelative));
            }
        }

        if (root.getFileName() != null && KNOWLEDGE_ITEM_UPLOAD_DIR.equals(root.getFileName().toString())) {
            String normalizedRelative = normalizeRelativePathValue(normalizedInput);
            if (StringUtils.isNotBlank(normalizedRelative)
                    && normalizedRelative.startsWith(KNOWLEDGE_ITEM_UPLOAD_DIR + PATH_SEPARATOR)) {
                String withoutPrefix = normalizedRelative.substring(KNOWLEDGE_ITEM_UPLOAD_DIR.length() + PATH_SEPARATOR.length());
                if (StringUtils.isNotBlank(withoutPrefix)) {
                    candidates.add(buildKnowledgeItemStoragePath(root, withoutPrefix));
                }
            }
        }

        Path fallback = null;
        for (Path candidate : candidates) {
            if (fallback == null) {
                fallback = candidate;
            }
            if (Files.exists(candidate) && Files.isRegularFile(candidate)) {
                return candidate;
            }
        }
        if (fallback == null) {
            throw new IllegalArgumentException("invalid knowledge item path");
        }
        return fallback;
    }

    private Path buildKnowledgeItemStoragePath(Path root, String relativePath) {
        String normalizedRelativePath = StringUtils.defaultString(relativePath).replace(PATH_SEPARATOR, java.io.File.separator);
        Path target = root.resolve(normalizedRelativePath).toAbsolutePath().normalize();
        if (!target.startsWith(root)) {
            throw new IllegalArgumentException("invalid knowledge item path");
        }
        return target;
    }

    private String extractRelativePathFromSegment(String rawPath, String segment) {
        if (StringUtils.isBlank(rawPath) || StringUtils.isBlank(segment)) {
            return null;
        }
        String normalized = rawPath.replace("\\", PATH_SEPARATOR).trim();
        while (normalized.startsWith(PATH_SEPARATOR)) {
            normalized = normalized.substring(1);
        }
        String segmentPrefix = segment + PATH_SEPARATOR;
        int index = normalized.indexOf(segmentPrefix);
        if (index < 0) {
            return segment.equals(normalized) ? segment : null;
        }
        return normalizeRelativePathValue(normalized.substring(index));
    }

    private String normalizeRelativePathValue(String relativePath) {
        if (StringUtils.isBlank(relativePath)) {
            return "";
        }
        String normalized = relativePath.replace("\\", PATH_SEPARATOR).trim();
        while (normalized.startsWith(PATH_SEPARATOR)) {
            normalized = normalized.substring(1);
        }
        while (normalized.endsWith(PATH_SEPARATOR)) {
            normalized = normalized.substring(0, normalized.length() - 1);
        }
        return normalized;
    }

    private Path resolveUploadRootPath() {
        String uploadDir = dataManagementProperties.getFileStorage().getUploadDir();
        return Paths.get(uploadDir).toAbsolutePath().normalize();
    }

    private void ensureParentDirectory(Path targetPath) {
        try {
            Path parent = targetPath.getParent();
            if (parent != null) {
                Files.createDirectories(parent);
            }
        } catch (IOException e) {
            throw new IllegalStateException("创建预览目录失败", e);
        }
    }

    private String trimError(String error) {
        if (StringUtils.isBlank(error)) {
            return "";
        }
        if (error.length() <= MAX_ERROR_LENGTH) {
            return error;
        }
        return error.substring(0, MAX_ERROR_LENGTH);
    }

    private String nowText() {
        return LocalDateTime.now().format(PREVIEW_TIME_FORMATTER);
    }
}
