package com.datamate.datamanagement.application;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.datamate.common.auth.application.ResourceAccessService;
import com.datamate.common.infrastructure.exception.BusinessAssert;
import com.datamate.common.infrastructure.exception.CommonErrorCode;
import com.datamate.common.interfaces.PagedResponse;
import com.datamate.datamanagement.common.enums.KnowledgeStatusType;
import com.datamate.datamanagement.domain.model.dataset.Tag;
import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet;
import com.datamate.datamanagement.infrastructure.exception.DataManagementErrorCode;
import com.datamate.datamanagement.infrastructure.persistence.mapper.TagMapper;
import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeSetRepository;
import com.datamate.datamanagement.interfaces.converter.KnowledgeConverter;
import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeSetRequest;
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery;
import com.datamate.datamanagement.interfaces.dto.KnowledgeSetResponse;
import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeSetRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

/**
 * 知识集应用服务
 */
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class KnowledgeSetApplicationService {
    private final KnowledgeSetRepository knowledgeSetRepository;
    private final TagMapper tagMapper;
    private final ResourceAccessService resourceAccessService;

    public KnowledgeSet createKnowledgeSet(CreateKnowledgeSetRequest request) {
        BusinessAssert.isTrue(knowledgeSetRepository.findByName(request.getName()) == null,
            DataManagementErrorCode.KNOWLEDGE_SET_ALREADY_EXISTS);

        KnowledgeSet knowledgeSet = KnowledgeConverter.INSTANCE.convertToKnowledgeSet(request);
        knowledgeSet.setId(UUID.randomUUID().toString());
        if (knowledgeSet.getStatus() == null) {
            knowledgeSet.setStatus(KnowledgeStatusType.DRAFT);
        }

        Set<Tag> tags = processTagNames(request.getTags());
        knowledgeSet.setTags(tags);

        validateEffectivePeriod(knowledgeSet.getValidFrom(), knowledgeSet.getValidTo());
        validatePublishable(knowledgeSet);

        knowledgeSetRepository.save(knowledgeSet);
        return knowledgeSet;
    }

    public KnowledgeSet updateKnowledgeSet(String setId, UpdateKnowledgeSetRequest request) {
        KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
        BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
        resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
        BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()),
            DataManagementErrorCode.KNOWLEDGE_SET_STATUS_ERROR);

        if (StringUtils.hasText(request.getName()) && !Objects.equals(request.getName(), knowledgeSet.getName())) {
            KnowledgeSet exist = knowledgeSetRepository.findByName(request.getName());
            BusinessAssert.isTrue(exist == null || Objects.equals(exist.getId(), setId),
                DataManagementErrorCode.KNOWLEDGE_SET_ALREADY_EXISTS);
            knowledgeSet.setName(request.getName());
        }

        if (request.getDescription() != null) {
            knowledgeSet.setDescription(request.getDescription());
        }

        if (request.getTags() != null) {
            knowledgeSet.setTags(processTagNames(request.getTags()));
        }

        if (request.getStatus() != null) {
            knowledgeSet.setStatus(request.getStatus());
        }

        if (request.getDomain() != null) {
            knowledgeSet.setDomain(request.getDomain());
        }
        if (request.getBusinessLine() != null) {
            knowledgeSet.setBusinessLine(request.getBusinessLine());
        }
        if (request.getOwner() != null) {
            knowledgeSet.setOwner(request.getOwner());
        }
        if (request.getValidFrom() != null || request.getValidTo() != null) {
            knowledgeSet.setValidFrom(request.getValidFrom());
            knowledgeSet.setValidTo(request.getValidTo());
        }
        if (request.getSourceType() != null) {
            knowledgeSet.setSourceType(request.getSourceType());
        }
        if (request.getSensitivity() != null) {
            knowledgeSet.setSensitivity(request.getSensitivity());
        }
        if (request.getMetadata() != null) {
            knowledgeSet.setMetadata(request.getMetadata());
        }

        validateEffectivePeriod(knowledgeSet.getValidFrom(), knowledgeSet.getValidTo());
        validatePublishable(knowledgeSet);

        knowledgeSetRepository.updateById(knowledgeSet);
        return knowledgeSet;
    }

    public void deleteKnowledgeSet(String setId) {
        KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
        BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
        resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
        knowledgeSetRepository.removeById(setId);
    }

    @Transactional(readOnly = true)
    public KnowledgeSet getKnowledgeSet(String setId) {
        KnowledgeSet knowledgeSet = knowledgeSetRepository.getById(setId);
        BusinessAssert.notNull(knowledgeSet, DataManagementErrorCode.KNOWLEDGE_SET_NOT_FOUND);
        resourceAccessService.assertOwnerAccess(knowledgeSet.getCreatedBy());
        return knowledgeSet;
    }

    @Transactional(readOnly = true)
    public PagedResponse<KnowledgeSetResponse> getKnowledgeSets(KnowledgeSetPagingQuery query) {
        IPage<KnowledgeSet> page = new Page<>(query.getPage(), query.getSize());
        String ownerFilterUserId = resourceAccessService.resolveOwnerFilterUserId();
        page = knowledgeSetRepository.findByCriteria(page, query, ownerFilterUserId);
        List<KnowledgeSetResponse> responses = KnowledgeConverter.INSTANCE.convertSetResponses(page.getRecords());
        return PagedResponse.of(responses, page.getCurrent(), page.getTotal(), page.getPages());
    }

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

    private void validatePublishable(KnowledgeSet knowledgeSet) {
        if (knowledgeSet.getStatus() != KnowledgeStatusType.PUBLISHED) {
            return;
        }
        BusinessAssert.isTrue(StringUtils.hasText(knowledgeSet.getDomain()), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.isTrue(StringUtils.hasText(knowledgeSet.getBusinessLine()), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.isTrue(StringUtils.hasText(knowledgeSet.getOwner()), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.isTrue(StringUtils.hasText(knowledgeSet.getSensitivity()), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.notNull(knowledgeSet.getSourceType(), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.notNull(knowledgeSet.getValidFrom(), CommonErrorCode.PARAM_ERROR);
        BusinessAssert.notNull(knowledgeSet.getValidTo(), CommonErrorCode.PARAM_ERROR);
    }

    private void validateEffectivePeriod(LocalDate validFrom, LocalDate validTo) {
        if (validFrom == null || validTo == null) {
            return;
        }
        BusinessAssert.isTrue(!validFrom.isAfter(validTo), CommonErrorCode.PARAM_ERROR);
    }

    private Set<Tag> processTagNames(List<String> tagNames) {
        if (CollectionUtils.isEmpty(tagNames)) {
            return new HashSet<>();
        }
        Set<Tag> tags = new HashSet<>();
        for (String tagName : tagNames) {
            if (!StringUtils.hasText(tagName)) {
                continue;
            }
            Tag tag = tagMapper.findByName(tagName);
            if (tag == null) {
                Tag newTag = new Tag(tagName, null, null, "#007bff");
                newTag.setUsageCount(0L);
                newTag.setId(UUID.randomUUID().toString());
                tagMapper.insert(newTag);
                tag = newTag;
            }
            tag.setUsageCount(tag.getUsageCount() == null ? 1L : tag.getUsageCount() + 1);
            tagMapper.updateUsageCount(tag.getId(), tag.getUsageCount());
            tags.add(tag);
        }
        return tags;
    }
}
