package com.njcn.rdms.module.project.framework.security.service;

import cn.hutool.core.collection.CollUtil;
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.module.project.constant.ObjectRoleConstants;
import com.njcn.rdms.module.project.constant.ProductObjectConstants;
import com.njcn.rdms.module.project.dal.dataobject.member.UserObjectRoleDO;
import com.njcn.rdms.module.project.dal.dataobject.product.ProductDO;
import com.njcn.rdms.module.project.dal.mysql.member.UserObjectRoleMapper;
import com.njcn.rdms.module.project.dal.mysql.product.ProductMapper;
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
import com.njcn.rdms.module.project.service.datascope.ObjectDataScope;
import com.njcn.rdms.module.project.service.datascope.ObjectDataScopeService;
import com.njcn.rdms.module.system.api.permission.ObjectPermissionApi;
import com.njcn.rdms.module.system.api.permission.UserVisibilityConfigApi;
import com.njcn.rdms.module.system.api.permission.dto.UserVisibilityConfigRespDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

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

/**
 * 产品对象权限服务。
 */
@Slf4j
@Service
public class ProductObjectPermissionService implements ObjectPermissionService {

    @Resource
    private UserObjectRoleMapper userObjectRoleMapper;
    @Resource
    private ObjectPermissionApi objectPermissionApi;
    @Resource
    private ProductMapper productMapper;
    @Resource
    private ObjectDataScopeService objectDataScopeService;
    @Resource
    private UserVisibilityConfigApi userVisibilityConfigApi;

    @Override
    public String getObjectType() {
        return ProductObjectConstants.OBJECT_TYPE;
    }

    @Override
    public boolean hasPermission(Long objectId, String permission) {
        // 当前产品域无 hasPermission 非抛模式调用场景，预留空实现。
        // 启用时参考 ProjectObjectPermissionService.hasPermission 同款实现。
        log.warn("[ProductObjectPermissionService.hasPermission] 未实现,默认返回 false;objectId={}, permission={}",
                objectId, permission);
        return false;
    }

    @Override
    public void checkPermissionOrVisibility(Long objectId, String permission) {
        if (objectId == null) {
            throw invalidParamException("对象编号不能为空");
        }
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 1) 成员 + 权限码
        List<UserObjectRoleDO> userRoles = userObjectRoleMapper
                .selectActiveListByObjectAndUserId(ProductObjectConstants.OBJECT_TYPE, objectId, loginUserId);
        if (!userRoles.isEmpty()) {
            String normalizedPermission = normalizePermission(permission);
            boolean allowed = userRoles.stream()
                    .map(UserObjectRoleDO::getRoleId)
                    .distinct()
                    .anyMatch(roleId -> getRolePermissions(roleId).contains(normalizedPermission));
            if (allowed) {
                return;
            }
        }
        // 2) 用户可见性配置兜底（按产品方向命中）
        if (visibleByUserVisibilityConfig(loginUserId, objectId)) {
            return;
        }
        // 3) 脱敏抛异常
        log.warn("[checkPermissionOrVisibility] 无读权限，objectId={}, permission={}", objectId, permission);
        throw exception(ErrorCodeConstants.PRODUCT_OBJECT_PERMISSION_DENIED);
    }

    private boolean visibleByUserVisibilityConfig(Long loginUserId, Long productId) {
        UserVisibilityConfigRespDTO cfg = userVisibilityConfigApi.getConfig(loginUserId).getCheckedData();
        if (cfg == null) {
            return false;
        }
        if ("all".equals(cfg.getType())) {
            return true;
        }
        if ("directions".equals(cfg.getType()) && CollUtil.isNotEmpty(cfg.getDirectionCodes())) {
            ProductDO product = productMapper.selectById(productId);
            return product != null && cfg.getDirectionCodes().contains(product.getDirectionCode());
        }
        return false;
    }

    @Override
    public void checkPermission(Long objectId, String permission, boolean memberOnly) {
        if (objectId == null) {
            throw invalidParamException("对象编号不能为空");
        }
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 多角色支持：拿 user 在对象内全部 ACTIVE 角色行
        List<UserObjectRoleDO> userRoles = userObjectRoleMapper
                .selectActiveListByObjectAndUserId(ProductObjectConstants.OBJECT_TYPE, objectId, loginUserId);
        if (userRoles.isEmpty()) {
            // 权限码/成员标记仅作技术诊断，落日志不外泄给用户（见 用户可见错误文案规范）
            log.warn("[checkPermission] 用户无对象角色，objectId={}, permission={}, memberOnly={}", objectId, permission, memberOnly);
            throw exception(ErrorCodeConstants.PRODUCT_OBJECT_PERMISSION_DENIED);
        }
        if (memberOnly) {
            return;
        }
        String normalizedPermission = normalizePermission(permission);
        // 任一角色含该权限码即放行（等价于多角色 union；短路求值）
        boolean allowed = userRoles.stream()
                .map(UserObjectRoleDO::getRoleId)
                .distinct()
                .anyMatch(roleId -> getRolePermissions(roleId).contains(normalizedPermission));
        if (!allowed) {
            log.warn("[checkPermission] 缺少对象权限码，objectId={}, permission={}", objectId, normalizedPermission);
            throw exception(ErrorCodeConstants.PRODUCT_OBJECT_PERMISSION_DENIED);
        }
    }

    @Override
    public void checkAccessible(Long objectId) {
        if (objectId == null) {
            throw invalidParamException("对象编号不能为空");
        }
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 显式成员：拥有任一 ACTIVE 对象角色即可访问
        List<UserObjectRoleDO> userRoles = userObjectRoleMapper
                .selectActiveListByObjectAndUserId(ProductObjectConstants.OBJECT_TYPE, objectId, loginUserId);
        if (!userRoles.isEmpty()) {
            return;
        }
        // 无显式角色：查对象拿 directionCode，按数据范围 scope 兜底（隐式 observer / 超管 ALL）
        ProductDO product = productMapper.selectById(objectId);
        if (product == null) {
            // spec §3.3 定稿：对象不存在一律 DENIED，不暴露存在性（技术诊断落 log.warn）
            log.warn("[checkAccessible] 对象不存在或无访问权，objectId={}", objectId);
            throw exception(ErrorCodeConstants.PRODUCT_OBJECT_PERMISSION_DENIED);
        }
        ObjectDataScope scope = objectDataScopeService.compute(loginUserId, ProductObjectConstants.OBJECT_TYPE);
        if (!scope.contains(objectId, product.getDirectionCode())) {
            log.warn("[checkAccessible] 无对象访问权，objectId={}", objectId);
            throw exception(ErrorCodeConstants.PRODUCT_OBJECT_PERMISSION_DENIED);
        }
    }

    private Set<String> getRolePermissions(Long roleId) {
        Set<String> permissions = objectPermissionApi
                .getObjectRolePermissions(roleId, ObjectRoleConstants.ROLE_SCOPE_OBJECT,
                        ProductObjectConstants.OBJECT_TYPE)
                .getCheckedData();
        if (permissions == null || permissions.isEmpty()) {
            return Collections.emptySet();
        }
        return permissions.stream()
                .filter(StringUtils::hasText)
                .map(String::trim)
                .collect(Collectors.toCollection(LinkedHashSet::new));
    }

    private String normalizePermission(String permission) {
        if (!StringUtils.hasText(permission)) {
            throw invalidParamException("对象权限码不能为空");
        }
        return permission.trim();
    }

}
