package tech.easyflow.ai.publish;

import com.fasterxml.jackson.databind.ObjectMapper;
import tech.easyflow.ai.enums.PublishStatus;
import tech.easyflow.approval.entity.vo.ApprovalSubmitRequest;
import tech.easyflow.approval.enums.ApprovalActionType;
import tech.easyflow.approval.service.ApprovalInstanceService;
import tech.easyflow.approval.service.ApprovalSubjectHandler;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.math.BigInteger;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

/**
 * AI 资源生命周期处理器抽象基类。
 *
 * @param <T> 资源类型
 */
public abstract class AbstractAiResourceLifecycleHandler<T> implements ApprovalSubjectHandler, AiResourceLifecycleHandler {

    protected static final String SNAPSHOT_KEY = "resourceSnapshot";
    protected static final String PREVIOUS_STATUS_KEY = "previousPublishStatus";

    private final ApprovalInstanceService approvalInstanceService;
    protected final ObjectMapper objectMapper;

    protected AbstractAiResourceLifecycleHandler(ApprovalInstanceService approvalInstanceService,
                                                 ObjectMapper objectMapper) {
        this.approvalInstanceService = approvalInstanceService;
        this.objectMapper = objectMapper;
    }

    /**
     * 加载资源，不存在时抛异常。
     *
     * @param resourceId 资源 ID
     * @return 资源
     */
    protected abstract T requireResource(BigInteger resourceId);

    /**
     * 校验资源管理权限。
     *
     * @param resource 资源
     */
    protected abstract void assertManagePermission(T resource);

    /**
     * 获取资源分类 ID。
     *
     * @param resource 资源
     * @return 分类 ID
     */
    protected abstract BigInteger getCategoryId(T resource);

    /**
     * 获取资源部门 ID。
     *
     * @param resource 资源
     * @return 部门 ID
     */
    protected abstract BigInteger getDeptId(T resource);

    /**
     * 获取资源标题。
     *
     * @param resource 资源
     * @return 资源标题
     */
    protected abstract String getTitle(T resource);

    /**
     * 获取当前发布状态。
     *
     * @param resource 资源
     * @return 发布状态
     */
    protected abstract PublishStatus getCurrentStatus(T resource);

    /**
     * 获取当前已发布快照。
     *
     * @param resource 资源
     * @return 已发布快照
     */
    protected abstract Map<String, Object> getPublishedSnapshot(T resource);

    /**
     * 构建当前草稿快照。
     *
     * @param resource 资源
     * @return 草稿快照
     */
    protected abstract Map<String, Object> buildResourceSnapshot(T resource);

    /**
     * 更新资源待审批状态。
     *
     * @param resourceId 资源 ID
     * @param publishStatus 发布状态
     * @param currentApprovalInstanceId 审批实例 ID
     */
    protected abstract void persistResourceState(BigInteger resourceId, PublishStatus publishStatus, BigInteger currentApprovalInstanceId);

    /**
     * 发布成功后写入已发布状态。
     *
     * @param resourceId 资源 ID
     * @param resourceSnapshot 已发布快照
     * @param operatorId 操作人 ID
     */
    protected abstract void publishResource(BigInteger resourceId, Map<String, Object> resourceSnapshot, BigInteger operatorId);

    /**
     * 下线成功后写入下线状态。
     *
     * @param resourceId 资源 ID
     */
    protected abstract void markResourceOffline(BigInteger resourceId);

    /**
     * 删除资源。
     *
     * @param resourceId 资源 ID
     */
    protected abstract void removeResource(BigInteger resourceId);

    /**
     * 返回资源中文名称。
     *
     * @return 资源名称
     */
    protected abstract String resourceLabel();

    /**
     * 返回当前动作摘要前缀。
     *
     * @param actionType 动作类型
     * @return 摘要前缀
     */
    protected String resolveActionLabel(ApprovalActionType actionType) {
        return switch (actionType) {
            case PUBLISH -> "发布";
            case OFFLINE -> "下线";
            case DELETE -> "删除";
        };
    }

    /**
     * 扩展下线快照内容。
     *
     * @param resource 资源
     * @param snapshot 当前快照副本
     */
    protected void enrichOfflineSnapshot(T resource, Map<String, Object> snapshot) {
    }

    /**
     * 删除前额外校验。
     *
     * @param resource 资源
     * @param currentStatus 当前状态
     */
    protected void validateDelete(T resource, PublishStatus currentStatus) {
    }

    /**
     * 下线成功后的额外副作用。
     *
     * @param resourceId 资源 ID
     */
    protected void afterOffline(BigInteger resourceId) {
    }

    /**
     * 删除成功前的额外副作用。
     *
     * @param resourceId 资源 ID
     */
    protected void beforeRemove(BigInteger resourceId) {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ApprovalSubmitRequest buildSubmitRequest(BigInteger resourceId, String actionType, BigInteger operatorId) {
        T resource = requireResource(resourceId);
        assertManagePermission(resource);
        if (approvalInstanceService.existsActiveInstance(resourceType(), resourceId)) {
            throw new BusinessException("当前" + resourceLabel() + "存在未结束审批，请先处理完成");
        }
        ApprovalActionType approvalActionType = ApprovalActionType.from(actionType);
        PublishStatus currentStatus = getCurrentStatus(resource);
        Map<String, Object> resourceSnapshot = resolveSubmitSnapshot(resource, approvalActionType, currentStatus);

        ApprovalSubmitRequest request = new ApprovalSubmitRequest();
        request.setResourceType(resourceType());
        request.setResourceId(resourceId);
        request.setActionType(approvalActionType.getCode());
        request.setApplicantId(operatorId);
        request.setCategoryId(getCategoryId(resource));
        request.setDeptId(getDeptId(resource));
        request.setSummary(resolveActionLabel(approvalActionType) + resourceLabel() + "：" + getTitle(resource));
        Map<String, Object> snapshot = new LinkedHashMap<>();
        snapshot.put(SNAPSHOT_KEY, resourceSnapshot);
        snapshot.put(PREVIOUS_STATUS_KEY, currentStatus.getCode());
        request.setSnapshotJson(snapshot);
        return request;
    }

    /**
     * 解析动作对应的提交快照。
     *
     * @param resource 资源
     * @param actionType 动作类型
     * @param currentStatus 当前状态
     * @return 提交快照
     */
    protected Map<String, Object> resolveSubmitSnapshot(T resource,
                                                        ApprovalActionType actionType,
                                                        PublishStatus currentStatus) {
        return switch (actionType) {
            case PUBLISH -> buildPublishSnapshot(resource, currentStatus);
            case OFFLINE -> buildOfflineSnapshot(resource, currentStatus);
            case DELETE -> buildDeleteSnapshot(resource, currentStatus);
        };
    }

    /**
     * 构建发布快照。
     *
     * @param resource 资源
     * @param currentStatus 当前状态
     * @return 发布快照
     */
    protected Map<String, Object> buildPublishSnapshot(T resource, PublishStatus currentStatus) {
        if (currentStatus != PublishStatus.DRAFT
                && currentStatus != PublishStatus.OFFLINE
                && currentStatus != PublishStatus.PUBLISHED) {
            throw new BusinessException("当前" + resourceLabel() + "状态不允许发布");
        }
        Map<String, Object> snapshot = buildResourceSnapshot(resource);
        if (currentStatus == PublishStatus.PUBLISHED && isSameSnapshot(snapshot, getPublishedSnapshot(resource))) {
            throw new BusinessException("当前内容与已发布版本一致，无需重新发布");
        }
        return snapshot;
    }

    /**
     * 构建下线快照。
     *
     * @param resource 资源
     * @param currentStatus 当前状态
     * @return 下线快照
     */
    protected Map<String, Object> buildOfflineSnapshot(T resource, PublishStatus currentStatus) {
        if (currentStatus != PublishStatus.PUBLISHED) {
            throw new BusinessException("当前" + resourceLabel() + "尚未发布，无法下线");
        }
        Map<String, Object> publishedSnapshot = getPublishedSnapshot(resource);
        if (publishedSnapshot == null || publishedSnapshot.isEmpty()) {
            throw new BusinessException("当前" + resourceLabel() + "缺少已发布快照，无法下线");
        }
        Map<String, Object> snapshot = new LinkedHashMap<>(publishedSnapshot);
        enrichOfflineSnapshot(resource, snapshot);
        return snapshot;
    }

    /**
     * 构建删除快照。
     *
     * @param resource 资源
     * @param currentStatus 当前状态
     * @return 删除快照
     */
    protected Map<String, Object> buildDeleteSnapshot(T resource, PublishStatus currentStatus) {
        if (currentStatus == PublishStatus.PUBLISHED) {
            throw new BusinessException("当前" + resourceLabel() + "已发布，请先下线后再删除");
        }
        if (currentStatus == PublishStatus.PUBLISH_PENDING
                || currentStatus == PublishStatus.OFFLINE_PENDING
                || currentStatus == PublishStatus.DELETE_PENDING) {
            throw new BusinessException("当前" + resourceLabel() + "存在进行中的审批，请先处理完成");
        }
        validateDelete(resource, currentStatus);
        return buildResourceSnapshot(resource);
    }

    /**
     * 执行审批通过后的真实动作。
     *
     * @param actionType 动作类型
     * @param resourceId 资源 ID
     * @param resourceSnapshot 快照
     * @param operatorId 操作人 ID
     */
    @Override
    public void applyApprovedAction(String actionType,
                                    BigInteger resourceId,
                                    Map<String, Object> resourceSnapshot,
                                    BigInteger operatorId) {
        ApprovalActionType normalizedAction = ApprovalActionType.from(actionType);
        if (normalizedAction == ApprovalActionType.PUBLISH) {
            publishResource(resourceId, resourceSnapshot, operatorId);
            return;
        }
        if (normalizedAction == ApprovalActionType.OFFLINE) {
            markResourceOffline(resourceId);
            afterOffline(resourceId);
            return;
        }
        beforeRemove(resourceId);
        removeResource(resourceId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void updatePendingState(BigInteger resourceId, PublishStatus publishStatus, BigInteger instanceId) {
        persistResourceState(resourceId, publishStatus, instanceId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void restoreState(BigInteger resourceId, PublishStatus previousStatus) {
        persistResourceState(resourceId, previousStatus, null);
    }

    /**
     * 从请求快照读取资源内容。
     *
     * @param snapshotJson 请求快照
     * @return 资源快照
     */
    @SuppressWarnings("unchecked")
    protected Map<String, Object> readResourceSnapshot(Map<String, Object> snapshotJson) {
        Object snapshot = snapshotJson == null ? null : snapshotJson.get(SNAPSHOT_KEY);
        if (!(snapshot instanceof Map<?, ?> map)) {
            throw new BusinessException("审批快照缺少" + resourceLabel() + "内容");
        }
        return new LinkedHashMap<>((Map<String, Object>) map);
    }

    /**
     * 判断当前草稿快照与已发布快照是否一致。
     *
     * @param currentSnapshot 当前快照
     * @param publishedSnapshot 已发布快照
     * @return 一致返回 true
     */
    protected boolean isSameSnapshot(Map<String, Object> currentSnapshot, Map<String, Object> publishedSnapshot) {
        if (publishedSnapshot == null || publishedSnapshot.isEmpty()) {
            return false;
        }
        return Objects.equals(
                objectMapper.valueToTree(currentSnapshot),
                objectMapper.valueToTree(publishedSnapshot)
        );
    }
}
