package com.ruoyi.ccdi.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.ccdi.project.constants.CcdiProjectStatusConstants;
import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.ProjectAccessScope;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectImportHistoryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectQueryDTO;
import com.ruoyi.ccdi.project.domain.dto.CcdiProjectSaveDTO;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankTagTask;
import com.ruoyi.ccdi.project.domain.event.CcdiProjectHistoryImportSubmittedEvent;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectHistoryListItemVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectStatusCountsVO;
import com.ruoyi.ccdi.project.domain.vo.CcdiProjectVO;
import com.ruoyi.ccdi.project.mapper.CcdiBankTagTaskMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.ccdi.project.service.CcdiProjectAccessService;
import com.ruoyi.ccdi.project.service.ICcdiProjectService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.lsfx.client.LsfxAnalysisClient;
import com.ruoyi.lsfx.domain.request.GetTokenRequest;
import com.ruoyi.lsfx.domain.response.GetTokenResponse;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 项目Service实现类
 *
 * @author ruoyi
 */
@Slf4j
@Service
public class CcdiProjectServiceImpl implements ICcdiProjectService {

    @Resource
    private CcdiProjectMapper projectMapper;

    @Resource
    private CcdiBankTagTaskMapper bankTagTaskMapper;

    @Resource
    private LsfxAnalysisClient lsfxAnalysisClient;

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    @Resource
    private CcdiProjectAccessService projectAccessService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
        // 1. 调用流水分析平台获取projectId
        Integer lsfxProjectId = callLsfxPlatform(dto.getProjectName());

        // 2. 创建项目实体
        CcdiProject project = new CcdiProject();
        BeanUtils.copyProperties(dto, project);

        // 3. 设置默认值和流水分析平台ID
        project.setStatus(CcdiProjectStatusConstants.PROCESSING);
        project.setIsArchived(0); // 未归档
        project.setTargetCount(0);
        project.setHighRiskCount(0);
        project.setMediumRiskCount(0);
        project.setLowRiskCount(0);
        project.setLsfxProjectId(lsfxProjectId);  // 设置流水分析平台ID

        // 4. 保存到数据库
        projectMapper.insert(project);
        log.info("【项目】项目状态初始化: projectId={}, projectName={}, newStatus={}, newStatusLabel={}, operator={}",
            project.getProjectId(), project.getProjectName(), project.getStatus(), resolveStatusLabel(project.getStatus()),
            resolveOperator(project.getCreateBy()));

        // 5. 返回VO
        CcdiProjectVO vo = new CcdiProjectVO();
        BeanUtils.copyProperties(project, vo);
        fillProjectExtraFields(project, vo);
        return vo;
    }

    @Override
    public CcdiProjectVO updateProject(CcdiProjectSaveDTO dto) {
        if (dto.getProjectId() == null) {
            throw new ServiceException("项目ID不能为空");
        }

        CcdiProject existingProject = projectMapper.selectById(dto.getProjectId());
        if (existingProject == null) {
            throw new ServiceException("项目不存在");
        }
        projectAccessService.assertCanOperate(dto.getProjectId());

        // 只更新允许修改的字段
        existingProject.setProjectName(dto.getProjectName());
        existingProject.setDescription(dto.getDescription());
        existingProject.setConfigType(dto.getConfigType());

        projectMapper.updateById(existingProject);

        CcdiProjectVO vo = new CcdiProjectVO();
        BeanUtils.copyProperties(existingProject, vo);
        fillProjectExtraFields(existingProject, vo);
        return vo;
    }

    @Override
    public boolean deleteProject(Long projectId, String operator) {
        projectAccessService.assertCanDelete(projectId);
        return projectMapper.markProjectDeleted(projectId, resolveOperator(operator)) > 0;
    }

    @Override
    public void restoreProject(Long projectId, String operator) {
        projectAccessService.assertCanManageDeletedProjects();
        int rows = projectMapper.restoreDeletedProject(projectId, resolveOperator(operator));
        if (rows <= 0) {
            throw new ServiceException("仅已删除项目允许恢复");
        }
        log.info("【项目】项目状态变更: projectId={}, oldStatus={}, oldStatusLabel={}, newStatus={}, newStatusLabel={}, operator={}",
            projectId, CcdiProjectStatusConstants.DELETED, resolveStatusLabel(CcdiProjectStatusConstants.DELETED),
            CcdiProjectStatusConstants.COMPLETED, resolveStatusLabel(CcdiProjectStatusConstants.COMPLETED),
            resolveOperator(operator));
    }

    @Override
    public CcdiProjectVO getProjectById(Long projectId) {
        projectAccessService.assertCanRead(projectId);
        CcdiProject project = projectMapper.selectById(projectId);
        if (project == null) {
            return null;
        }
        CcdiProjectVO vo = new CcdiProjectVO();
        BeanUtils.copyProperties(project, vo);
        fillProjectExtraFields(project, vo);
        return vo;
    }

    @Override
    public Page<CcdiProjectVO> selectProjectPage(Page<CcdiProjectVO> page, CcdiProjectQueryDTO queryDTO) {
        ProjectAccessScope scope = projectAccessService.buildCurrentScope();
        Page<CcdiProjectVO> result = projectMapper.selectProjectPage(page, queryDTO, scope);
        fillProjectExtraFields(result.getRecords());
        return result;
    }

    @Override
    public List<CcdiProjectHistoryListItemVO> listHistoryProjects(CcdiProjectQueryDTO queryDTO) {
        return projectMapper.selectHistoryProjects(queryDTO, projectAccessService.buildCurrentScope());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CcdiProjectVO importFromHistory(CcdiProjectImportHistoryDTO dto, String operator) {
        projectAccessService.assertSourceProjectsReadable(dto.getSourceProjectIds());
        CcdiProjectSaveDTO saveDTO = new CcdiProjectSaveDTO();
        saveDTO.setProjectName(dto.getProjectName());
        saveDTO.setDescription(dto.getDescription());
        saveDTO.setConfigType("default");
        CcdiProjectVO project = createProject(saveDTO);
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                applicationEventPublisher.publishEvent(
                    new CcdiProjectHistoryImportSubmittedEvent(project.getProjectId(), project.getLsfxProjectId(), dto, operator)
                );
            }
        });
        return project;
    }

    @Override
    public CcdiProjectStatusCountsVO getStatusCounts() {
        CcdiProjectStatusCountsVO vo = new CcdiProjectStatusCountsVO();
        ProjectAccessScope scope = projectAccessService.buildCurrentScope();

        // 统计全部项目
        LambdaQueryWrapper<CcdiProject> baseWrapper = buildScopeWrapper(scope);
        Long totalCount = projectMapper.selectCount(baseWrapper);
        vo.setAll(totalCount);

        // 统计进行中项目（状态0）
        Long status0Count = projectMapper.selectCount(buildScopeWrapper(scope)
            .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.PROCESSING));
        vo.setStatus0(status0Count);

        // 统计已完成项目（状态1）
        Long status1Count = projectMapper.selectCount(buildScopeWrapper(scope)
            .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.COMPLETED));
        vo.setStatus1(status1Count);

        // 统计已归档项目（状态2）
        Long status2Count = projectMapper.selectCount(buildScopeWrapper(scope)
            .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.ARCHIVED));
        vo.setStatus2(status2Count);

        Long status3Count = projectMapper.selectCount(buildScopeWrapper(scope)
            .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAGGING));
        vo.setStatus3(status3Count);

        Long status4Count = projectMapper.selectCount(buildScopeWrapper(scope)
            .eq(CcdiProject::getStatus, CcdiProjectStatusConstants.TAG_FAILED));
        vo.setStatus4(status4Count);

        Long status5Count = 0L;
        if (isProjectAdmin(scope)) {
            status5Count = projectMapper.selectDeletedProjectCount(scope);
        }
        vo.setStatus5(status5Count);

        return vo;
    }

    @Override
    public void archiveProject(Long projectId, String operator) {
        CcdiProject project = getRequiredProject(projectId);
        projectAccessService.assertCanOperate(projectId);
        if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) {
            throw new ServiceException("项目已归档，无需重复操作");
        }
        if (!CcdiProjectStatusConstants.COMPLETED.equals(project.getStatus())) {
            throw new ServiceException("仅已完成项目允许归档");
        }
        project.setStatus(CcdiProjectStatusConstants.ARCHIVED);
        project.setIsArchived(1);
        project.setUpdateBy(operator);
        project.setUpdateTime(new Date());
        projectMapper.updateById(project);
        log.info("【项目】项目状态变更: projectId={}, projectName={}, oldStatus={}, oldStatusLabel={}, newStatus={}, newStatusLabel={}, operator={}",
            project.getProjectId(), project.getProjectName(), CcdiProjectStatusConstants.COMPLETED,
            resolveStatusLabel(CcdiProjectStatusConstants.COMPLETED), CcdiProjectStatusConstants.ARCHIVED,
            resolveStatusLabel(CcdiProjectStatusConstants.ARCHIVED), resolveOperator(operator));
    }

    @Override
    public void updateProjectStatus(Long projectId, String status, String operator) {
        CcdiProject project = getRequiredProject(projectId);
        String oldStatus = project.getStatus();
        if (CcdiProjectStatusConstants.DELETED.equals(project.getStatus())) {
            throw new ServiceException("已删除项目不允许更新状态");
        }
        if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())
            && !CcdiProjectStatusConstants.ARCHIVED.equals(status)) {
            throw new ServiceException("已归档项目不允许重新进入打标流程");
        }
        project.setStatus(status);
        project.setUpdateBy(operator);
        project.setUpdateTime(new Date());
        projectMapper.updateById(project);
        if (!Objects.equals(oldStatus, status)) {
            log.info("【项目】项目状态变更: projectId={}, projectName={}, oldStatus={}, oldStatusLabel={}, newStatus={}, newStatusLabel={}, operator={}",
                project.getProjectId(), project.getProjectName(), oldStatus, resolveStatusLabel(oldStatus),
                status, resolveStatusLabel(status), resolveOperator(operator));
        }
    }

    @Override
    public void ensureProjectCanStartTagging(Long projectId) {
        CcdiProject project = getRequiredProject(projectId);
        if (CcdiProjectStatusConstants.DELETED.equals(project.getStatus())) {
            throw new ServiceException("已删除项目不允许重新进入打标流程");
        }
        if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) {
            throw new ServiceException("已归档项目不允许重新进入打标流程");
        }
    }

    @Override
    public void ensureProjectNotArchived(Long projectId, String message) {
        CcdiProject project = getRequiredProject(projectId);
        if (CcdiProjectStatusConstants.DELETED.equals(project.getStatus())) {
            throw new ServiceException("已删除项目暂不允许操作");
        }
        if (CcdiProjectStatusConstants.ARCHIVED.equals(project.getStatus())) {
            throw new ServiceException(message);
        }
    }

    @Override
    public void ensureProjectWritable(Long projectId, String message) {
        CcdiProject project = getRequiredProject(projectId);
        if (CcdiProjectStatusConstants.DELETED.equals(project.getStatus())) {
            throw new ServiceException("已删除项目暂不允许操作");
        }
        if (CcdiProjectStatusConstants.TAGGING.equals(project.getStatus())) {
            throw new ServiceException(message);
        }
    }

    private CcdiProject getRequiredProject(Long projectId) {
        CcdiProject project = projectMapper.selectById(projectId);
        if (project == null) {
            throw new ServiceException("项目不存在");
        }
        return project;
    }

    private String resolveStatusLabel(String status) {
        return switch (status) {
            case CcdiProjectStatusConstants.PROCESSING -> "进行中";
            case CcdiProjectStatusConstants.COMPLETED -> "已完成";
            case CcdiProjectStatusConstants.ARCHIVED -> "已归档";
            case CcdiProjectStatusConstants.TAGGING -> "打标中";
            case CcdiProjectStatusConstants.TAG_FAILED -> "打标失败";
            case CcdiProjectStatusConstants.DELETED -> "已删除";
            default -> "未知";
        };
    }

    private void fillLatestTagFailure(CcdiProject project, CcdiProjectVO vo) {
        if (!CcdiProjectStatusConstants.TAG_FAILED.equals(project.getStatus())) {
            return;
        }
        CcdiBankTagTask latestFailedTask = bankTagTaskMapper.selectLatestFailedTaskByProjectId(project.getProjectId());
        if (latestFailedTask == null) {
            return;
        }
        vo.setLatestTagTaskErrorMessage(latestFailedTask.getErrorMessage());
        vo.setLatestTagTaskEndTime(latestFailedTask.getEndTime());
    }

    private LambdaQueryWrapper<CcdiProject> buildScopeWrapper(ProjectAccessScope scope) {
        LambdaQueryWrapper<CcdiProject> wrapper = new LambdaQueryWrapper<>();
        wrapper.ne(CcdiProject::getStatus, CcdiProjectStatusConstants.DELETED);
        if (scope != null && !scope.isViewAllProjects()) {
            wrapper.eq(CcdiProject::getCreateBy, scope.getUsername());
        }
        return wrapper;
    }

    private boolean isProjectAdmin(ProjectAccessScope scope) {
        return scope != null && (scope.isSuperAdmin() || scope.isProjectManager());
    }

    private void fillProjectExtraFields(List<CcdiProjectVO> records) {
        if (records == null || records.isEmpty()) {
            return;
        }
        ProjectAccessScope scope = projectAccessService.buildCurrentScope();
        for (CcdiProjectVO vo : records) {
            fillProjectAccessFields(vo, scope);
        }
    }

    private void fillProjectExtraFields(CcdiProject project, CcdiProjectVO vo) {
        fillLatestTagFailure(project, vo);
        fillProjectAccessFields(vo, projectAccessService.buildCurrentScope());
    }

    private void fillProjectAccessFields(CcdiProjectVO vo, ProjectAccessScope scope) {
        if (vo == null || scope == null) {
            return;
        }
        boolean ownedByCurrentUser = Objects.equals(scope.getUsername(), vo.getCreateBy());
        vo.setOwnedByCurrentUser(ownedByCurrentUser);
        vo.setCanOperate(scope.isSuperAdmin() || ownedByCurrentUser);
        vo.setCanDelete(isProjectAdmin(scope) || ownedByCurrentUser);
    }

    private String resolveOperator(String operator) {
        return StringUtils.hasText(operator) ? operator : "system";
    }

    /**
     * 调用流水分析平台获取projectId
     *
     * @param projectName 项目名称
     * @return 流水分析平台项目ID
     * @throws ServiceException 调用失败或响应无效时抛出
     */
    private Integer callLsfxPlatform(String projectName) {
        // 构建请求参数
        GetTokenRequest request = new GetTokenRequest();
        request.setProjectNo("902000_" + System.currentTimeMillis());
        request.setEntityName(projectName);
        request.setUserId("902001");
        request.setUserName("902001");
        request.setRole("VIEWER");
        request.setOrgCode("902000");
        request.setAnalysisType("-1");
        request.setDepartmentCode("902000");

        // 调用流水分析平台（异常处理和日志已在 LsfxAnalysisClient 中完成）
        GetTokenResponse response = lsfxAnalysisClient.getToken(request);

        // 业务层校验：确保响应有效
        if (response == null || response.getData() == null) {
            throw new ServiceException("流水分析平台响应数据为空");
        }

        if (response.getData().getProjectId() == null) {
            throw new ServiceException("流水分析平台返回的projectId为空");
        }

        // 校验返回码
        if (!"200".equals(response.getCode())) {
            throw new ServiceException("流水分析平台返回错误: " + response.getMessage());
        }

        return response.getData().getProjectId();
    }
}
