package com.ruoyi.ccdi.project.service;

import com.ruoyi.ccdi.project.domain.CcdiProject;
import com.ruoyi.ccdi.project.domain.ProjectAccessScope;
import com.ruoyi.ccdi.project.constants.CcdiProjectStatusConstants;
import com.ruoyi.ccdi.project.domain.entity.CcdiBankStatement;
import com.ruoyi.ccdi.project.domain.entity.CcdiEvidence;
import com.ruoyi.ccdi.project.domain.entity.CcdiFileUploadRecord;
import com.ruoyi.ccdi.project.mapper.CcdiBankStatementMapper;
import com.ruoyi.ccdi.project.mapper.CcdiEvidenceMapper;
import com.ruoyi.ccdi.project.mapper.CcdiFileUploadRecordMapper;
import com.ruoyi.ccdi.project.mapper.CcdiProjectMapper;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

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

/**
 * 项目访问控制。
 */
@Service
public class CcdiProjectAccessService {

    private static final String ROLE_ADMIN = "admin";

    private static final String ROLE_MANAGER = "manager";

    @Resource
    private CcdiProjectMapper projectMapper;

    @Resource
    private CcdiBankStatementMapper bankStatementMapper;

    @Resource
    private CcdiFileUploadRecordMapper fileUploadRecordMapper;

    @Resource
    private CcdiEvidenceMapper evidenceMapper;

    public ProjectAccessScope buildCurrentScope() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        String username = SecurityUtils.getUsername();
        boolean superAdmin = isSuperAdmin(loginUser);
        boolean projectManager = hasRole(loginUser, ROLE_MANAGER);
        return new ProjectAccessScope(username, superAdmin || projectManager, superAdmin, projectManager);
    }

    public boolean canOperate(CcdiProject project) {
        if (project == null) {
            return false;
        }
        ProjectAccessScope scope = buildCurrentScope();
        return scope.isSuperAdmin() || Objects.equals(scope.getUsername(), project.getCreateBy());
    }

    public boolean canDelete(CcdiProject project) {
        if (project == null) {
            return false;
        }
        ProjectAccessScope scope = buildCurrentScope();
        return isProjectAdmin(scope) || Objects.equals(scope.getUsername(), project.getCreateBy());
    }

    public void assertCanRead(Long projectId) {
        CcdiProject project = getRequiredProject(projectId);
        assertProjectNotDeleted(project);
        ProjectAccessScope scope = buildCurrentScope();
        if (scope.isViewAllProjects() || Objects.equals(scope.getUsername(), project.getCreateBy())) {
            return;
        }
        throw new ServiceException("无权查看该项目");
    }

    public void assertCanOperate(Long projectId) {
        CcdiProject project = getRequiredProject(projectId);
        assertProjectNotDeleted(project);
        if (canOperate(project)) {
            return;
        }
        throw new ServiceException("无权操作该项目");
    }

    public void assertCanDelete(Long projectId) {
        CcdiProject project = getRequiredProject(projectId);
        assertProjectNotDeleted(project);
        if (canDelete(project)) {
            return;
        }
        throw new ServiceException("无权删除该项目");
    }

    public void assertCanManageDeletedProjects() {
        if (isProjectAdmin(buildCurrentScope())) {
            return;
        }
        throw new ServiceException("无权管理已删除项目");
    }

    public void assertCanReadByBankStatementId(Long bankStatementId) {
        CcdiBankStatement statement = getRequiredBankStatement(bankStatementId);
        assertCanRead(statement.getProjectId());
    }

    public void assertCanReadByFileRecordId(Long fileRecordId) {
        CcdiFileUploadRecord record = getRequiredFileRecord(fileRecordId);
        assertCanRead(record.getProjectId());
    }

    public void assertCanOperateByFileRecordId(Long fileRecordId) {
        CcdiFileUploadRecord record = getRequiredFileRecord(fileRecordId);
        assertCanOperate(record.getProjectId());
    }

    public void assertCanReadByEvidenceId(Long evidenceId) {
        CcdiEvidence evidence = getRequiredEvidence(evidenceId);
        assertCanRead(evidence.getProjectId());
    }

    public void assertSourceProjectsReadable(List<Long> sourceProjectIds) {
        if (CollectionUtils.isEmpty(sourceProjectIds)) {
            return;
        }
        for (Long sourceProjectId : sourceProjectIds) {
            assertCanRead(sourceProjectId);
        }
    }

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

    private void assertProjectNotDeleted(CcdiProject project) {
        if (project == null) {
            return;
        }
        if (CcdiProjectStatusConstants.DELETED.equals(project.getStatus()) || "2".equals(project.getDelFlag())) {
            throw new ServiceException("项目已删除");
        }
    }

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

    private CcdiBankStatement getRequiredBankStatement(Long bankStatementId) {
        if (bankStatementId == null) {
            throw new ServiceException("流水ID不能为空");
        }
        CcdiBankStatement statement = bankStatementMapper.selectById(bankStatementId);
        if (statement == null) {
            throw new ServiceException("流水记录不存在");
        }
        return statement;
    }

    private CcdiFileUploadRecord getRequiredFileRecord(Long fileRecordId) {
        if (fileRecordId == null) {
            throw new ServiceException("文件记录ID不能为空");
        }
        CcdiFileUploadRecord record = fileUploadRecordMapper.selectById(fileRecordId);
        if (record == null) {
            throw new ServiceException("文件记录不存在");
        }
        return record;
    }

    private CcdiEvidence getRequiredEvidence(Long evidenceId) {
        if (evidenceId == null) {
            throw new ServiceException("证据ID不能为空");
        }
        CcdiEvidence evidence = evidenceMapper.selectById(evidenceId);
        if (evidence == null) {
            throw new ServiceException("证据不存在");
        }
        return evidence;
    }

    private boolean isSuperAdmin(LoginUser loginUser) {
        if (loginUser == null) {
            return false;
        }
        if (SecurityUtils.isAdmin(loginUser.getUserId())) {
            return true;
        }
        return hasRole(loginUser, ROLE_ADMIN);
    }

    private boolean hasRole(LoginUser loginUser, String roleKey) {
        if (loginUser == null || loginUser.getUser() == null
            || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {
            return false;
        }
        for (SysRole role : loginUser.getUser().getRoles()) {
            if (roleKey.equals(role.getRoleKey())) {
                return true;
            }
        }
        return false;
    }
}
