package com.ycwl.basic.pipeline.core;

import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import lombok.extern.slf4j.Slf4j;

/**
 * 通用 Stage 抽象基类，封装可选性判断与钩子逻辑。
 */
@Slf4j
public abstract class AbstractPipelineStage<C extends PipelineContext> implements PipelineStage<C> {

    @Override
    public final boolean shouldExecute(C context) {
        StageConfig config = getStageConfig();
        if (config != null) {
            String stageId = config.stageId();
            StageOptionalMode mode = config.optionalMode();

            if (mode == StageOptionalMode.FORCE_ON) {
                return shouldExecuteByBusinessLogic(context);
            }

            if (mode == StageOptionalMode.SUPPORT) {
                boolean externalEnabled = context.isStageEnabled(stageId, config.defaultEnabled());
                if (!externalEnabled) {
                    log.debug("[{}] Stage 被外部配置禁用", stageId);
                    return false;
                }
            }
        }

        return shouldExecuteByBusinessLogic(context);
    }

    protected boolean shouldExecuteByBusinessLogic(C context) {
        return true;
    }

    protected void beforeExecute(C context) {
        log.debug("[{}] 开始执行", getName());
    }

    protected void afterExecute(C context, StageResult<C> result) {
        if (result.isSuccess()) {
            log.debug("[{}] 执行成功: {}", getName(), result.getMessage());
        } else if (result.isSkipped()) {
            log.debug("[{}] 已跳过: {}", getName(), result.getMessage());
        } else if (result.isDegraded()) {
            log.warn("[{}] 降级执行: {}", getName(), result.getMessage());
        } else {
            log.error("[{}] 执行失败: {}", getName(), result.getMessage(), result.getException());
        }
    }

    protected abstract StageResult<C> doExecute(C context);

    @Override
    public final StageResult<C> execute(C context) {
        beforeExecute(context);
        StageResult<C> result = doExecute(context);
        afterExecute(context, result);
        return result;
    }
}
