package com.ycwl.basic.pipeline.core;

import com.ycwl.basic.pipeline.annotation.StageConfig;
import com.ycwl.basic.pipeline.enums.StageOptionalMode;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class PipelineTest {

    @Test
    void execute_shouldRunStagesAndHooks() {
        RecordingContext context = new RecordingContext();

        SimpleStage stage1 = new SimpleStage("stage1", ctx -> {
            SimpleStage dynamic = new SimpleStage("stage1_dynamic", c -> StageResult.success());
            return StageResult.successWithNext("add dynamic", dynamic);
        });
        SimpleStage stage2 = new SimpleStage("stage2", ctx -> StageResult.success());

        Pipeline<RecordingContext> pipeline = new PipelineBuilder<RecordingContext>("recording")
            .addStage(stage1)
            .addStage(stage2)
            .build();

        boolean result = pipeline.execute(context);

        assertTrue(result);
        assertEquals(
            List.of("before", "stage1", "stage1_dynamic", "stage2", "after", "cleanup"),
            context.getEvents()
        );
    }

    @Test
    void execute_shouldStopOnFailureAndSkipAfterHook() {
        RecordingContext context = new RecordingContext();

        SimpleStage ok = new SimpleStage("ok", ctx -> StageResult.success());
        SimpleStage fail = new SimpleStage("fail", ctx -> StageResult.failed("boom"));
        SimpleStage neverRun = new SimpleStage("never", ctx -> StageResult.success());

        Pipeline<RecordingContext> pipeline = new PipelineBuilder<RecordingContext>("failing")
            .addStage(ok)
            .addStage(fail)
            .addStage(neverRun)
            .build();

        boolean result = pipeline.execute(context);

        assertFalse(result);
        assertEquals(
            List.of("before", "ok", "fail", "cleanup"),
            context.getEvents()
        );
    }

    @Test
    void stageConfigShouldRespectExternalSwitch() {
        RecordingContext context = new RecordingContext();
        context.setStageState("optional_stage", false);

        OptionalStage optional = new OptionalStage();
        Pipeline<RecordingContext> pipeline = new PipelineBuilder<RecordingContext>("optional")
            .addStage(optional)
            .build();

        boolean result = pipeline.execute(context);

        assertTrue(result);
        assertEquals(
            List.of("before", "after", "cleanup"),
            context.getEvents()
        );
    }

    /**
     * 记录执行轨迹的 Context，便于断言 Pipeline 生命周期钩子与 Stage 执行顺序。
     */
    static class RecordingContext implements PipelineContext {
        private final List<String> events = new ArrayList<>();
        private final Map<String, Boolean> stageStates = new HashMap<>();

        @Override
        public void beforePipeline() {
            events.add("before");
        }

        @Override
        public void afterPipeline() {
            events.add("after");
        }

        @Override
        public void cleanup() {
            events.add("cleanup");
        }

        @Override
        public boolean isStageEnabled(String stageId, boolean defaultEnabled) {
            return stageStates.getOrDefault(stageId, defaultEnabled);
        }

        void setStageState(String stageId, boolean enabled) {
            stageStates.put(stageId, enabled);
        }

        List<String> getEvents() {
            return events;
        }
    }

    /**
     * 简化的 Stage 实现，支持自定义执行逻辑与业务前置条件。
     */
    static class SimpleStage extends AbstractPipelineStage<RecordingContext> {
        private final String name;
        private final Function<RecordingContext, StageResult<RecordingContext>> executor;
        private final Predicate<RecordingContext> condition;

        SimpleStage(String name, Function<RecordingContext, StageResult<RecordingContext>> executor) {
            this(name, executor, ctx -> true);
        }

        SimpleStage(String name,
                    Function<RecordingContext, StageResult<RecordingContext>> executor,
                    Predicate<RecordingContext> condition) {
            this.name = name;
            this.executor = executor;
            this.condition = condition;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        protected boolean shouldExecuteByBusinessLogic(RecordingContext context) {
            return condition.test(context);
        }

        @Override
        protected StageResult<RecordingContext> doExecute(RecordingContext context) {
            context.getEvents().add(name);
            return executor.apply(context);
        }
    }

    /**
     * 带 StageConfig 的 Stage，用于验证 SUPPORT 模式下的开关控制。
     */
    @StageConfig(stageId = "optional_stage", optionalMode = StageOptionalMode.SUPPORT, defaultEnabled = true)
    static class OptionalStage extends SimpleStage {

        OptionalStage() {
            super("optional", ctx -> StageResult.success());
        }
    }
}
