package com.njcn.rdms.module.project.service.review;

import com.njcn.rdms.module.project.constant.ProductObjectConstants;
import com.njcn.rdms.module.project.constant.ProjectObjectConstants;
import com.njcn.rdms.module.project.controller.admin.product.vo.requirement.ProductRequirementStatusActionReqVO;
import com.njcn.rdms.module.project.controller.admin.project.vo.requirement.ProjectRequirementStatusActionReqVO;
import com.njcn.rdms.module.project.controller.admin.review.vo.RequirementReviewRespVO;
import com.njcn.rdms.module.project.controller.admin.review.vo.RequirementReviewSubmitReqVO;
import com.njcn.rdms.module.project.dal.dataobject.product.ProductRequirementDO;
import com.njcn.rdms.module.project.dal.dataobject.project.ProjectRequirementDO;
import com.njcn.rdms.module.project.dal.dataobject.review.RequirementReviewDO;
import com.njcn.rdms.module.project.dal.mysql.product.ProductRequirementMapper;
import com.njcn.rdms.module.project.dal.mysql.project.ProjectRequirementMapper;
import com.njcn.rdms.module.project.dal.mysql.review.RequirementReviewMapper;
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
import com.njcn.rdms.module.project.framework.attachment.AttachmentFileIdResolver;
import com.njcn.rdms.module.project.framework.attachment.AttachmentValidator;
import com.njcn.rdms.module.project.framework.security.annotation.CheckObjectPermission;
import com.njcn.rdms.module.project.service.product.ProductRequirementService;
import com.njcn.rdms.module.project.service.project.ProjectRequirementService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;

import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;

/**
 * 需求评审 Service 实现类。
 */
@Service
public class RequirementReviewServiceImpl implements RequirementReviewService {

    private static final String PRODUCT_REQUIREMENT_OBJECT_TYPE = "product_requirement";
    private static final String PROJECT_REQUIREMENT_OBJECT_TYPE = "project_requirement";
    private static final int CONCLUSION_PASS = 0;
    private static final int CONCLUSION_REJECT = 1;
    private static final String ACTION_PASS_REVIEW = "pass_review";
    private static final String ACTION_REJECT_REVIEW = "reject_review";

    @Resource
    private RequirementReviewMapper reviewMapper;
    @Resource
    private ProductRequirementMapper productRequirementMapper;
    @Resource
    private ProjectRequirementMapper projectRequirementMapper;
    @Resource
    private ProductRequirementService productRequirementService;
    @Resource
    private ProjectRequirementService projectRequirementService;
    @Resource
    private AttachmentFileIdResolver attachmentFileIdResolver;

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CheckObjectPermission(objectType = ProductObjectConstants.OBJECT_TYPE, objectId = "#reqVO.productId",
            permission = ProductObjectConstants.PERMISSION_REVIEW)
    public Long submitProductRequirementReview(RequirementReviewSubmitReqVO reqVO) {
        validateProductRequirement(reqVO);
        validateConclusion(reqVO.getConclusion(), true);
        validateReviewNotExists(PRODUCT_REQUIREMENT_OBJECT_TYPE, reqVO.getRequirementId(), true);
        AttachmentValidator.validate(reqVO.getAttachments());
        attachmentFileIdResolver.resolve(reqVO.getAttachments());

        RequirementReviewDO review = buildReview(reqVO, PRODUCT_REQUIREMENT_OBJECT_TYPE);
        reviewMapper.insert(review);

        ProductRequirementStatusActionReqVO actionReqVO = new ProductRequirementStatusActionReqVO();
        actionReqVO.setId(reqVO.getRequirementId());
        actionReqVO.setProductId(reqVO.getProductId());
        actionReqVO.setActionCode(resolveReviewAction(reqVO.getConclusion()));
        productRequirementService.changeRequirementStatusForReview(actionReqVO);
        return review.getId();
    }

    @Override
    @CheckObjectPermission(objectType = ProductObjectConstants.OBJECT_TYPE, objectId = "#productId", accessible = true)
    public RequirementReviewRespVO getProductRequirementReview(Long productId, Long requirementId) {
        ProductRequirementDO requirement = productRequirementMapper.selectById(requirementId);
        if (requirement == null || !Objects.equals(requirement.getProductId(), productId)) {
            throw exception(ErrorCodeConstants.REQUIREMENT_NOT_EXISTS);
        }
        RequirementReviewDO review = reviewMapper.selectByObjectTypeAndRequirementId(
                PRODUCT_REQUIREMENT_OBJECT_TYPE, requirementId);
        if (review == null) {
            throw exception(ErrorCodeConstants.REQUIREMENT_REVIEW_NOT_EXISTS);
        }
        return buildRespVO(review);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CheckObjectPermission(objectType = ProjectObjectConstants.OBJECT_TYPE, objectId = "#reqVO.projectId",
            permission = ProjectObjectConstants.PERMISSION_REVIEW)
    public Long submitProjectRequirementReview(RequirementReviewSubmitReqVO reqVO) {
        validateProjectRequirement(reqVO);
        validateConclusion(reqVO.getConclusion(), false);
        validateReviewNotExists(PROJECT_REQUIREMENT_OBJECT_TYPE, reqVO.getRequirementId(), false);
        AttachmentValidator.validate(reqVO.getAttachments());
        attachmentFileIdResolver.resolve(reqVO.getAttachments());

        RequirementReviewDO review = buildReview(reqVO, PROJECT_REQUIREMENT_OBJECT_TYPE);
        reviewMapper.insert(review);

        ProjectRequirementStatusActionReqVO actionReqVO = new ProjectRequirementStatusActionReqVO();
        actionReqVO.setId(reqVO.getRequirementId());
        actionReqVO.setProjectId(reqVO.getProjectId());
        actionReqVO.setActionCode(resolveReviewAction(reqVO.getConclusion()));
        projectRequirementService.changeRequirementStatusForReview(actionReqVO);
        return review.getId();
    }

    @Override
    @CheckObjectPermission(objectType = ProjectObjectConstants.OBJECT_TYPE, objectId = "#projectId", accessible = true)
    public RequirementReviewRespVO getProjectRequirementReview(Long projectId, Long requirementId) {
        ProjectRequirementDO requirement = projectRequirementMapper.selectById(requirementId);
        if (requirement == null || !Objects.equals(requirement.getProjectId(), projectId)) {
            throw exception(ErrorCodeConstants.PROJECT_REQUIREMENT_NOT_EXISTS);
        }
        RequirementReviewDO review = reviewMapper.selectByObjectTypeAndRequirementId(
                PROJECT_REQUIREMENT_OBJECT_TYPE, requirementId);
        if (review == null) {
            throw exception(ErrorCodeConstants.PROJECT_REQUIREMENT_REVIEW_NOT_EXISTS);
        }
        return buildRespVO(review);
    }

    private void validateProductRequirement(RequirementReviewSubmitReqVO reqVO) {
        if (reqVO.getProductId() == null) {
            throw invalidParamException("产品编号不能为空");
        }
        ProductRequirementDO requirement = productRequirementMapper.selectById(reqVO.getRequirementId());
        if (requirement == null || !Objects.equals(requirement.getProductId(), reqVO.getProductId())) {
            throw exception(ErrorCodeConstants.REQUIREMENT_NOT_EXISTS);
        }
    }

    private void validateProjectRequirement(RequirementReviewSubmitReqVO reqVO) {
        if (reqVO.getProjectId() == null) {
            throw invalidParamException("项目编号不能为空");
        }
        ProjectRequirementDO requirement = projectRequirementMapper.selectById(reqVO.getRequirementId());
        if (requirement == null || !Objects.equals(requirement.getProjectId(), reqVO.getProjectId())) {
            throw exception(ErrorCodeConstants.PROJECT_REQUIREMENT_NOT_EXISTS);
        }
    }

    private void validateReviewNotExists(String objectType, Long requirementId, boolean productRequirement) {
        RequirementReviewDO review = reviewMapper.selectByObjectTypeAndRequirementId(objectType, requirementId);
        if (review != null) {
            throw exception(productRequirement
                    ? ErrorCodeConstants.REQUIREMENT_REVIEW_ALREADY_EXISTS
                    : ErrorCodeConstants.PROJECT_REQUIREMENT_REVIEW_ALREADY_EXISTS);
        }
    }

    private void validateConclusion(Integer conclusion, boolean productRequirement) {
        if (!Objects.equals(conclusion, CONCLUSION_PASS) && !Objects.equals(conclusion, CONCLUSION_REJECT)) {
            throw exception(productRequirement
                    ? ErrorCodeConstants.REQUIREMENT_REVIEW_CONCLUSION_INVALID
                    : ErrorCodeConstants.PROJECT_REQUIREMENT_REVIEW_CONCLUSION_INVALID);
        }
    }

    private RequirementReviewDO buildReview(RequirementReviewSubmitReqVO reqVO, String objectType) {
        RequirementReviewDO review = new RequirementReviewDO();
        review.setObjectType(objectType);
        review.setRequirementId(reqVO.getRequirementId());
        review.setOperatorId(reqVO.getOperatorId());
        review.setConclusion(reqVO.getConclusion());
        review.setReviewContent(reqVO.getReviewContent());
        review.setRequirementEstimatedHours(reqVO.getRequirementEstimatedHours());
        review.setAttendees(reqVO.getAttendees());
        review.setAttachments(reqVO.getAttachments());
        review.setReviewTime(toReviewTime(reqVO.getReviewTime()));
        return review;
    }

    private RequirementReviewRespVO buildRespVO(RequirementReviewDO review) {
        RequirementReviewRespVO respVO = new RequirementReviewRespVO();
        respVO.setId(review.getId());
        respVO.setObjectType(review.getObjectType());
        respVO.setRequirementId(review.getRequirementId());
        respVO.setOperatorId(review.getOperatorId());
        respVO.setConclusion(review.getConclusion());
        respVO.setReviewContent(review.getReviewContent());
        respVO.setRequirementEstimatedHours(review.getRequirementEstimatedHours());
        respVO.setAttendees(review.getAttendees());
        respVO.setAttachments(review.getAttachments());
        respVO.setReviewTime(review.getReviewTime() == null ? null : review.getReviewTime().toLocalDate());
        respVO.setCreateTime(review.getCreateTime());
        respVO.setUpdateTime(review.getUpdateTime());
        return respVO;
    }

    private String resolveReviewAction(Integer conclusion) {
        return Objects.equals(conclusion, CONCLUSION_PASS) ? ACTION_PASS_REVIEW : ACTION_REJECT_REVIEW;
    }

    private LocalDateTime toReviewTime(LocalDate reviewTime) {
        LocalDate date = reviewTime == null ? LocalDate.now() : reviewTime;
        return date.atStartOfDay();
    }

}
