package tech.easyflow.ai.service.impl;

import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.entity.WorkflowShare;
import tech.easyflow.ai.enums.KnowledgeShareStatus;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.ai.enums.WorkflowSharePurpose;
import tech.easyflow.ai.mapper.WorkflowShareMapper;
import tech.easyflow.ai.service.WorkflowService;
import tech.easyflow.ai.service.WorkflowShareService;
import tech.easyflow.ai.share.WorkflowSharePolicy;
import tech.easyflow.ai.vo.WorkflowShareCreateResult;
import tech.easyflow.common.cache.RedisLockExecutor;
import tech.easyflow.common.web.exceptions.BusinessException;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 工作流协作分享服务实现。
 */
@Service
public class WorkflowShareServiceImpl extends ServiceImpl<WorkflowShareMapper, WorkflowShare>
        implements WorkflowShareService {

    private static final String LOCK_KEY_PREFIX = "easyflow:lock:workflow-share:";
    private static final Duration LOCK_WAIT_TIMEOUT = Duration.ofSeconds(2);
    private static final Duration LOCK_LEASE_TIMEOUT = Duration.ofSeconds(10);

    @Resource
    private WorkflowService workflowService;

    @Resource
    private RedisLockExecutor redisLockExecutor;

    @Resource
    private PlatformTransactionManager transactionManager;

    /**
     * {@inheritDoc}
     */
    @Override
    public WorkflowShareCreateResult createUrlShare(
            BigInteger workflowId,
            BigInteger tenantId,
            BigInteger deptId,
            BigInteger operatorId,
            String baseUrl
    ) {
        return createShare(
                workflowId,
                tenantId,
                deptId,
                operatorId,
                baseUrl,
                WorkflowSharePurpose.COLLABORATION,
                false
        );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public WorkflowShareCreateResult createChatShare(
            BigInteger workflowId,
            BigInteger tenantId,
            BigInteger deptId,
            BigInteger operatorId,
            String baseUrl
    ) {
        return createShare(
                workflowId,
                tenantId,
                deptId,
                operatorId,
                baseUrl,
                WorkflowSharePurpose.CHAT,
                true
        );
    }

    /**
     * 在用途级分布式锁内创建分享。
     *
     * @param workflowId 工作流 ID
     * @param tenantId 租户 ID
     * @param deptId 部门 ID
     * @param operatorId 操作人账号 ID
     * @param baseUrl 分享基础 URL
     * @param purpose 分享用途
     * @param requirePublished 是否要求严格发布态
     * @return 创建结果
     */
    private WorkflowShareCreateResult createShare(
            BigInteger workflowId,
            BigInteger tenantId,
            BigInteger deptId,
            BigInteger operatorId,
            String baseUrl,
            WorkflowSharePurpose purpose,
            boolean requirePublished
    ) {
        return redisLockExecutor.executeWithLock(
                LOCK_KEY_PREFIX + workflowId + ":" + purpose.name(),
                LOCK_WAIT_TIMEOUT,
                LOCK_LEASE_TIMEOUT,
                () -> {
                    // 在释放分布式锁前完成事务提交，避免并发请求观察到未提交的旧分享状态。
                    TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
                    return transactionTemplate.execute(status -> createOrReplaceShare(
                            workflowId,
                            tenantId,
                            deptId,
                            operatorId,
                            baseUrl,
                            purpose,
                            requirePublished
                    ));
                }
        );
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public WorkflowShare assertUrlShareAccess(
            String shareKey,
            BigInteger workflowId,
            BigInteger tenantId
    ) {
        if (workflowId == null) {
            throw invalidShare();
        }
        WorkflowShare share = resolveUrlShare(shareKey, tenantId);
        if (!workflowId.equals(share.getWorkflowId())) {
            throw invalidShare();
        }
        return share;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public WorkflowShare resolveUrlShare(String shareKey, BigInteger tenantId) {
        return resolveShare(shareKey, tenantId, WorkflowSharePurpose.COLLABORATION);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public WorkflowShare assertChatShareAccess(
            String shareKey,
            BigInteger workflowId,
            BigInteger tenantId
    ) {
        if (workflowId == null) {
            throw invalidShare();
        }
        WorkflowShare share = resolveChatShare(shareKey, tenantId);
        if (!workflowId.equals(share.getWorkflowId())) {
            throw invalidShare();
        }
        return share;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public WorkflowShare resolveChatShare(String shareKey, BigInteger tenantId) {
        return resolveShare(shareKey, tenantId, WorkflowSharePurpose.CHAT);
    }

    /**
     * 按用途校验并解析分享。
     *
     * @param shareKey 原始分享密钥
     * @param tenantId 当前租户 ID
     * @param purpose 分享用途
     * @return 有效分享记录
     */
    private WorkflowShare resolveShare(
            String shareKey,
            BigInteger tenantId,
            WorkflowSharePurpose purpose
    ) {
        if (shareKey == null || shareKey.isBlank() || tenantId == null) {
            throw invalidShare();
        }
        WorkflowShare share = getOne(QueryWrapper.create()
                .eq(WorkflowShare::getShareKeyHash, WorkflowSharePolicy.hashShareKey(shareKey))
                .eq(WorkflowShare::getSharePurpose, purpose.name())
                .eq(WorkflowShare::getStatus, KnowledgeShareStatus.ENABLED.name()));
        if (share == null || !tenantId.equals(share.getTenantId())) {
            throw invalidShare();
        }
        if (share.getExpiresAt() == null || !share.getExpiresAt().after(new Date())) {
            throw new BusinessException(403, 403, "工作流分享链接已过期");
        }
        Workflow workflow = workflowService.getById(share.getWorkflowId());
        if (workflow == null || !tenantId.equals(workflow.getTenantId())) {
            throw invalidShare();
        }
        if (purpose == WorkflowSharePurpose.CHAT && !isStrictlyPublished(workflow)) {
            throw new BusinessException(409, 409, "工作流尚未发布或已下线");
        }
        return share;
    }

    /**
     * 在锁保护下创建或替换工作流的唯一分享记录。
     *
     * @param workflowId 工作流 ID
     * @param tenantId 租户 ID
     * @param deptId 部门 ID
     * @param operatorId 操作人账号 ID
     * @param baseUrl 工作流分享基础 URL
     * @param purpose 分享用途
     * @param requirePublished 是否要求严格发布态
     * @return 创建结果
     */
    private WorkflowShareCreateResult createOrReplaceShare(
            BigInteger workflowId,
            BigInteger tenantId,
            BigInteger deptId,
            BigInteger operatorId,
            String baseUrl,
            WorkflowSharePurpose purpose,
            boolean requirePublished
    ) {
        Workflow workflow = workflowService.getById(workflowId);
        if (workflow == null || tenantId == null || !tenantId.equals(workflow.getTenantId())) {
            throw new BusinessException("工作流不存在");
        }
        if (requirePublished && !isStrictlyPublished(workflow)) {
            throw new BusinessException(409, 409, "仅已发布工作流可创建对话分享");
        }

        String shareKey = UUID.randomUUID().toString().replace("-", "");
        Date now = new Date();
        Date expiresAt = purpose == WorkflowSharePurpose.CHAT
                ? WorkflowSharePolicy.defaultChatExpiresAt(now)
                : WorkflowSharePolicy.defaultExpiresAt(now);
        invalidateExistingShares(workflowId, purpose, operatorId, now);
        WorkflowShare share = new WorkflowShare();
        share.setWorkflowId(workflowId);
        share.setSharePurpose(purpose.name());
        share.setTenantId(tenantId);
        share.setDeptId(deptId);
        share.setShareKeyHash(WorkflowSharePolicy.hashShareKey(shareKey));
        share.setStatus(KnowledgeShareStatus.ENABLED.name());
        share.setExpiresAt(expiresAt);
        share.setCreated(now);
        share.setCreatedBy(operatorId);
        share.setModified(now);
        share.setModifiedBy(operatorId);
        save(share);

        WorkflowShareCreateResult result = new WorkflowShareCreateResult();
        result.setId(share.getId());
        result.setShareKey(shareKey);
        result.setShareUrl(buildShareUrl(baseUrl, shareKey));
        result.setExpiresAt(expiresAt);
        return result;
    }

    /**
     * 使工作流已有的有效分享记录失效。
     *
     * @param workflowId 工作流 ID
     * @param purpose 分享用途
     * @param operatorId 操作人账号 ID
     * @param now 当前时间
     */
    private void invalidateExistingShares(
            BigInteger workflowId,
            WorkflowSharePurpose purpose,
            BigInteger operatorId,
            Date now
    ) {
        List<WorkflowShare> activeShares = list(QueryWrapper.create()
                .eq(WorkflowShare::getWorkflowId, workflowId)
                .eq(WorkflowShare::getSharePurpose, purpose.name())
                .eq(WorkflowShare::getStatus, KnowledgeShareStatus.ENABLED.name()));
        for (WorkflowShare activeShare : activeShares) {
            WorkflowShare update = new WorkflowShare();
            update.setId(activeShare.getId());
            update.setStatus(KnowledgeShareStatus.DISABLED.name());
            update.setModified(now);
            update.setModifiedBy(operatorId);
            updateById(update);
        }
    }

    /**
     * 构建仅包含分享密钥的协作 URL。
     *
     * @param baseUrl 分享基础 URL
     * @param shareKey 原始分享密钥
     * @return 分享 URL
     */
    private String buildShareUrl(String baseUrl, String shareKey) {
        if (baseUrl == null || baseUrl.isBlank()) {
            return null;
        }
        return baseUrl + (baseUrl.contains("?") ? "&" : "?") + "shareKey=" + shareKey;
    }

    /**
     * 构建无效分享异常。
     *
     * @return 无效分享异常
     */
    private BusinessException invalidShare() {
        return new BusinessException(403, 403, "工作流分享链接无效");
    }

    /**
     * 判断工作流是否处于严格发布态且存在发布快照。
     *
     * @param workflow 工作流
     * @return 可按发布快照运行时返回 {@code true}
     */
    private boolean isStrictlyPublished(Workflow workflow) {
        return workflow != null
                && PublishStatus.PUBLISHED.getCode().equals(workflow.getPublishStatus())
                && workflow.getPublishedSnapshotJson() != null
                && !workflow.getPublishedSnapshotJson().isEmpty();
    }
}
