package com.datamate.datamanagement.application;

import com.datamate.common.infrastructure.exception.BusinessAssert;
import com.datamate.common.infrastructure.exception.CommonErrorCode;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItemDirectory;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
import com.datamate.datamanagement.infrastructure.exception.DataManagementErrorCode;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemDirectoryRepository;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeDirectoryRequest;
import com.datamate.datamanagement.interfaces.dto.KnowledgeDirectoryQuery;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.UUID;

/**
 * 知识条目目录应用服务
 */
@Service
@Transactional
@RequiredArgsConstructor
public class KnowledgeDirectoryApplicationService {
    private static final String PATH_SEPARATOR = "/";
    private static final String INVALID_PATH_SEGMENT = "..";

    private final KnowledgeItemDirectoryRepository knowledgeItemDirectoryRepository;
    private final KnowledgeItemRepository knowledgeItemRepository;
    private final KnowledgeSetRepository knowledgeSetRepository;

    @Transactional(readOnly = true)
    public List<KnowledgeItemDirectory> getKnowledgeDirectories(String setId, KnowledgeDirectoryQuery query) {
        BusinessAssert.notNull(query, CommonErrorCode.PARAM_ERROR);
        query.setSetId(setId);
        return knowledgeItemDirectoryRepository.findByCriteria(query);
    }

    public KnowledgeItemDirectory createKnowledgeDirectory(String setId, CreateKnowledgeDirectoryRequest request) {
        BusinessAssert.notNull(request, CommonErrorCode.PARAM_ERROR);
        KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
        BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
            DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);

        String directoryName = normalizeDirectoryName(request.getDirectoryName());
        validateDirectoryName(directoryName);

        String parentPrefix = normalizeRelativePathPrefix(request.getParentPrefix());
        String relativePath = normalizeRelativePathValue(parentPrefix + directoryName);
        validateRelativePath(relativePath);

        BusinessAssert.isTrue(!knowledgeItemRepository.existsBySetIdAndRelativePath(setId, relativePath),
            CommonErrorCode.PARAM_ERROR);

        KnowledgeItemDirectory existing = knowledgeItemDirectoryRepository.findBySetIdAndPath(setId, relativePath);
        if (existing != null) {
            return existing;
        }

        KnowledgeItemDirectory directory = new KnowledgeItemDirectory();
        directory.setId(UUID.randomUUID().toString());
        directory.setSetId(setId);
        directory.setName(directoryName);
        directory.setRelativePath(relativePath);
        knowledgeItemDirectoryRepository.save(directory);
        return directory;
    }

    public void deleteKnowledgeDirectory(String setId, String relativePath) {
        KnowledgeSet knowledgeSet = requireKnowledgeSet(setId);
        BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
            DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);

        String normalized = normalizeRelativePathValue(relativePath);
        validateRelativePath(normalized);

        knowledgeItemRepository.removeByRelativePathPrefix(setId, normalized);
        knowledgeItemDirectoryRepository.removeByRelativePathPrefix(setId, normalized);
    }

    private KnowledgeSet requireKnowledgeSet(String setId) {
        KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
        BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
        return knowledgeSet;
    }

    private boolean isReadOnlyStatus(KnowledgeStatusType status) {
        return status == KnowledgeStatusType.ARCHIVED || status == KnowledgeStatusType.DEPRECATED;
    }

    private String normalizeDirectoryName(String name) {
        return StringUtils.trimToEmpty(name);
    }

    private void validateDirectoryName(String name) {
        BusinessAssert.isTrue(StringUtils.isNotBlank(name), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.isTrue(!name.contains(PATH_SEPARATOR), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.isTrue(!name.contains("\\"), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.isTrue(!name.contains(INVALID_PATH_SEGMENT), CommonErrorCode.PARAM_ERROR);
    }

    private void validateRelativePath(String relativePath) {
        BusinessAssert.isTrue(StringUtils.isNotBlank(relativePath), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.isTrue(!relativePath.contains(INVALID_PATH_SEGMENT), CommonErrorCode.PARAM_ERROR);
    }

    private String normalizeRelativePathPrefix(String prefix) {
        if (StringUtils.isBlank(prefix)) {
            return "";
        }
        String normalized = prefix.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);
        }
        if (StringUtils.isBlank(normalized)) {
            return "";
        }
        validateRelativePath(normalized);
        return normalized + PATH_SEPARATOR;
    }

    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;
    }
}
