package tech.easyflow.ai.service.impl;

import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import tech.easyflow.ai.enums.KnowledgeApiPermissionScope;
import tech.easyflow.ai.enums.KnowledgeShareActionScope;
import tech.easyflow.ai.service.KnowledgeSharePermissionService;
import tech.easyflow.common.cache.RedisLockExecutor;
import tech.easyflow.common.cache.RedisLockExecutor.LockHandle;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.entity.SysApiKey;
import tech.easyflow.system.entity.SysApiKeyResource;
import tech.easyflow.system.entity.SysApiKeyResourceMapping;
import tech.easyflow.system.service.SysApiKeyResourceMappingService;
import tech.easyflow.system.service.SysApiKeyResourceService;
import tech.easyflow.system.service.SysApiKeyService;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 知识库分享权限服务实现。
 */
@Service
public class KnowledgeSharePermissionServiceImpl implements KnowledgeSharePermissionService {

    public static final String RESOURCE_TYPE_KNOWLEDGE = "KNOWLEDGE";

    private static final String PERMISSION_LOCK_PREFIX =
        "easyflow:lock:knowledge-api-permission:";
    private static final Duration PERMISSION_LOCK_WAIT = Duration.ofSeconds(2);
    private static final Duration PERMISSION_LOCK_LEASE = Duration.ofSeconds(15);

    private static final Map<String, List<String>> API_SCOPE_URI_MAPPING = new LinkedHashMap<>();
    private static final Map<String, List<String>> LEGACY_ACTION_URI_MAPPING = new LinkedHashMap<>();

    static {
        API_SCOPE_URI_MAPPING.put(KnowledgeApiPermissionScope.KNOWLEDGE_READ.name(), List.of(
            "/public-api/knowledge-share/detail",
            "/public-api/knowledge-share/search",
            "/public-api/knowledge-share/document/page",
            "/public-api/knowledge-share/document/download",
            "/public-api/knowledge-share/documentChunk/page",
            "/public-api/knowledge-share/faq/page",
            "/public-api/knowledge-share/faq/detail",
            "/public-api/knowledge-share/faq/exportExcel"
        ));
        API_SCOPE_URI_MAPPING.put(KnowledgeApiPermissionScope.KNOWLEDGE_IMPORT.name(), List.of(
            "/public-api/knowledge-share/document/import/batch",
            "/public-api/knowledge-share/document/import/batch/status",
            "/public-api/knowledge-share/document/import/batch/retry",
            "/public-api/knowledge-share/document/import/analyze",
            "/public-api/knowledge-share/document/import/preview",
            "/public-api/knowledge-share/document/import/commit",
            "/public-api/knowledge-share/document/import/task/create",
            "/public-api/knowledge-share/document/import/task/detail",
            "/public-api/knowledge-share/document/import/task/preview",
            "/public-api/knowledge-share/document/import/task/startIndex",
            "/public-api/knowledge-share/document/import/task/retryParse",
            "/public-api/knowledge-share/document/import/task/retryIndex",
            "/public-api/knowledge-share/faq/importExcel",
            "/public-api/knowledge-share/faq/downloadImportTemplate"
        ));
        API_SCOPE_URI_MAPPING.put(KnowledgeApiPermissionScope.KNOWLEDGE_MAINTENANCE.name(), List.of(
            "/public-api/knowledge-share/document/remove",
            "/public-api/knowledge-share/documentChunk/update",
            "/public-api/knowledge-share/documentChunk/remove",
            "/public-api/knowledge-share/faq/save",
            "/public-api/knowledge-share/faq/update",
            "/public-api/knowledge-share/faq/remove"
        ));

        LEGACY_ACTION_URI_MAPPING.put(KnowledgeShareActionScope.VIEW.name(), List.of(
            "/public-api/knowledge-share/detail",
            "/public-api/knowledge-share/document/page",
            "/public-api/knowledge-share/document/download",
            "/public-api/knowledge-share/document/import/task/detail",
            "/public-api/knowledge-share/documentChunk/page",
            "/public-api/knowledge-share/faq/page",
            "/public-api/knowledge-share/faq/detail"
        ));
        LEGACY_ACTION_URI_MAPPING.put(KnowledgeShareActionScope.SEARCH.name(), List.of(
            "/public-api/knowledge-share/search"
        ));
        LEGACY_ACTION_URI_MAPPING.put(KnowledgeShareActionScope.CONTENT_CREATE.name(), List.of(
            "/public-api/knowledge-share/document/import/analyze",
            "/public-api/knowledge-share/document/import/preview",
            "/public-api/knowledge-share/document/import/commit",
            "/public-api/knowledge-share/document/import/task/create",
            "/public-api/knowledge-share/document/import/task/preview",
            "/public-api/knowledge-share/document/import/task/startIndex",
            "/public-api/knowledge-share/document/import/task/retryParse",
            "/public-api/knowledge-share/document/import/task/retryIndex",
            "/public-api/knowledge-share/document/import/batch",
            "/public-api/knowledge-share/document/import/batch/status",
            "/public-api/knowledge-share/document/import/batch/retry",
            "/public-api/knowledge-share/faq/save"
        ));
        LEGACY_ACTION_URI_MAPPING.put(KnowledgeShareActionScope.CONTENT_UPDATE.name(), List.of(
            "/public-api/knowledge-share/documentChunk/update",
            "/public-api/knowledge-share/faq/update"
        ));
        LEGACY_ACTION_URI_MAPPING.put(KnowledgeShareActionScope.CONTENT_DELETE.name(), List.of(
            "/public-api/knowledge-share/document/remove",
            "/public-api/knowledge-share/documentChunk/remove",
            "/public-api/knowledge-share/faq/remove"
        ));
        LEGACY_ACTION_URI_MAPPING.put(KnowledgeShareActionScope.IMPORT_EXPORT.name(), List.of(
            "/public-api/knowledge-share/faq/importExcel",
            "/public-api/knowledge-share/faq/exportExcel",
            "/public-api/knowledge-share/faq/downloadImportTemplate"
        ));
    }

    @Resource
    private SysApiKeyService sysApiKeyService;
    @Resource
    private SysApiKeyResourceService resourceService;
    @Resource
    private SysApiKeyResourceMappingService mappingService;
    @Resource
    private RedisLockExecutor redisLockExecutor;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void grantApiShare(BigInteger apiKeyId, BigInteger knowledgeId, Set<String> actionScopes) {
        if (apiKeyId == null) {
            throw new BusinessException("系统访问令牌不能为空");
        }
        if (knowledgeId == null) {
            throw new BusinessException("知识库不能为空");
        }
        SysApiKey apiKey = sysApiKeyService.getById(apiKeyId);
        if (apiKey == null) {
            throw new BusinessException("系统访问令牌不存在");
        }
        Set<String> normalizedScopes = KnowledgeShareActionScope.normalize(actionScopes);
        if (normalizedScopes.isEmpty()) {
            throw new BusinessException("动作范围不能为空");
        }

        Runnable releaseLock = acquirePermissionMutationLock(apiKeyId);
        try {
            mappingService.removeScopedMappings(
                apiKeyId,
                RESOURCE_TYPE_KNOWLEDGE,
                knowledgeId
            );
            List<SysApiKeyResourceMapping> rows = new ArrayList<>();
            for (String legacyScope : normalizedScopes) {
                List<String> uris = LEGACY_ACTION_URI_MAPPING.get(legacyScope);
                if (uris == null || uris.isEmpty()) {
                    continue;
                }
                for (String uri : uris) {
                    rows.add(buildMapping(
                        apiKeyId,
                        knowledgeId,
                        uri,
                        requireApiScope(uri)
                    ));
                }
            }
            if (!rows.isEmpty()) {
                mappingService.saveBatch(rows);
            }
        } finally {
            releaseLock.run();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void replaceApiShareEnabled(BigInteger apiKeyId, boolean enabled) {
        replaceApiPermissions(apiKeyId, enabled, enabled, false);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void replaceApiPermissions(BigInteger apiKeyId,
                                      boolean readEnabled,
                                      boolean importEnabled,
                                      boolean maintenanceEnabled) {
        if (apiKeyId == null) {
            throw new BusinessException("系统访问令牌不能为空");
        }
        SysApiKey apiKey = sysApiKeyService.getById(apiKeyId);
        if (apiKey == null) {
            throw new BusinessException("系统访问令牌不存在");
        }
        Runnable releaseLock = acquirePermissionMutationLock(apiKeyId);
        try {
            // 全局开关只替换全局授权，保留分享页配置的指定知识库权限。
            mappingService.remove(
                QueryWrapper.create()
                    .eq(SysApiKeyResourceMapping::getApiKeyId, apiKeyId)
                    .eq(
                        SysApiKeyResourceMapping::getResourceType,
                        RESOURCE_TYPE_KNOWLEDGE
                    )
                    .isNull(
                        SysApiKeyResourceMapping::getResourceTargetId
                    )
            );
            Set<String> enabledScopes = KnowledgeApiPermissionScope.enabledScopes(
                readEnabled,
                importEnabled,
                maintenanceEnabled
            );
            if (enabledScopes.isEmpty()) {
                return;
            }

            List<SysApiKeyResourceMapping> rows = new ArrayList<>();
            for (String scope : enabledScopes) {
                List<String> uris = API_SCOPE_URI_MAPPING.get(scope);
                if (uris == null || uris.isEmpty()) {
                    continue;
                }
                for (String uri : uris) {
                    rows.add(buildMapping(apiKeyId, null, uri, scope));
                }
            }
            if (!rows.isEmpty()) {
                mappingService.saveBatch(rows);
            }
        } finally {
            releaseLock.run();
        }
    }

    @Override
    public Set<String> getApiPermissionScopes(BigInteger apiKeyId) {
        if (apiKeyId == null) {
            return Set.of();
        }
        List<SysApiKeyResourceMapping> mappings = mappingService.list(
            QueryWrapper.create()
                .select(SysApiKeyResourceMapping::getActionScope)
                .eq(SysApiKeyResourceMapping::getApiKeyId, apiKeyId)
                .eq(SysApiKeyResourceMapping::getResourceType, RESOURCE_TYPE_KNOWLEDGE)
                .isNull(SysApiKeyResourceMapping::getResourceTargetId)
                .in(
                    SysApiKeyResourceMapping::getActionScope,
                    API_SCOPE_URI_MAPPING.keySet()
                )
        );
        if (mappings == null || mappings.isEmpty()) {
            return Set.of();
        }
        Set<String> scopes = new java.util.LinkedHashSet<>();
        for (SysApiKeyResourceMapping mapping : mappings) {
            if (mapping.getActionScope() != null) {
                scopes.add(mapping.getActionScope());
            }
        }
        return scopes;
    }

    @Override
    public void assertApiShare(BigInteger apiKeyId, String requestUri, BigInteger knowledgeId, String actionScope) {
        if (apiKeyId == null || knowledgeId == null) {
            throw new BusinessException("API 分享鉴权参数不完整");
        }
        if (!API_SCOPE_URI_MAPPING.containsKey(actionScope)) {
            throw new IllegalArgumentException("未知的知识库 API 权限范围");
        }
        sysApiKeyService.checkResourceScope(apiKeyId, requestUri, RESOURCE_TYPE_KNOWLEDGE, knowledgeId, actionScope);
    }

    /**
     * 构造单条知识库 API 权限映射。
     *
     * @param apiKeyId 访问令牌 ID
     * @param knowledgeId 知识库 ID；为空表示全局授权
     * @param uri 请求 URI
     * @param scope 产品权限 Scope
     * @return 权限映射
     */
    private SysApiKeyResourceMapping buildMapping(BigInteger apiKeyId,
                                                  BigInteger knowledgeId,
                                                  String uri,
                                                  String scope) {
        SysApiKeyResource resource = ensureResource(uri);
        SysApiKeyResourceMapping row = new SysApiKeyResourceMapping();
        row.setApiKeyId(apiKeyId);
        row.setApiKeyResourceId(resource.getId());
        row.setResourceType(RESOURCE_TYPE_KNOWLEDGE);
        row.setResourceTargetId(knowledgeId);
        row.setActionScope(scope);
        return row;
    }

    /**
     * 按 URI 获取唯一的产品权限 Scope。
     *
     * @param uri 请求 URI
     * @return 产品权限 Scope
     */
    private String requireApiScope(String uri) {
        for (Map.Entry<String, List<String>> entry : API_SCOPE_URI_MAPPING.entrySet()) {
            if (entry.getValue().contains(uri)) {
                return entry.getKey();
            }
        }
        throw new IllegalArgumentException("知识库接口未归类: " + uri);
    }

    /**
     * 获取或创建固定 URI 对应的 API 资源。
     *
     * @param requestInterface 请求 URI
     * @return API 资源
     */
    private SysApiKeyResource ensureResource(String requestInterface) {
        QueryWrapper wrapper = QueryWrapper.create()
            .eq(SysApiKeyResource::getRequestInterface, requestInterface);
        SysApiKeyResource resource = resourceService.getOne(wrapper);
        if (resource != null) {
            return resource;
        }
        resource = new SysApiKeyResource();
        resource.setRequestInterface(requestInterface);
        resource.setTitle("知识库分享接口");
        resourceService.save(resource);
        return resource;
    }

    /**
     * 获取访问令牌知识库权限变更锁，并在事务完成后释放。
     *
     * @param apiKeyId 访问令牌 ID
     * @return 非事务直接调用时使用的释放动作
     */
    private Runnable acquirePermissionMutationLock(BigInteger apiKeyId) {
        LockHandle handle = redisLockExecutor.tryAcquire(
            PERMISSION_LOCK_PREFIX + apiKeyId,
            PERMISSION_LOCK_WAIT,
            PERMISSION_LOCK_LEASE
        );
        if (handle == null) {
            throw new BusinessException("访问令牌权限正在更新，请稍后重试");
        }
        if (TransactionSynchronizationManager.isSynchronizationActive()
            && TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(
                new TransactionSynchronization() {
                    @Override
                    public void afterCompletion(int status) {
                        handle.release();
                    }
                }
            );
            return () -> {
            };
        }
        return handle::release;
    }
}
