import { Action, BranchCondition, BranchOperator, RouterExecutionType } from '@activepieces/shared'
import { ExecutionVerdict, FlowExecutorContext } from '../../src/lib/handler/context/flow-execution-context'
import { flowExecutor } from '../../src/lib/handler/flow-executor'
import { buildCodeAction, buildPieceAction, buildRouterWithOneCondition, generateMockEngineConstants } from './test-helper'

function executeRouterActionWithOneCondition(children: Action[], conditions: (BranchCondition | null)[], executionType: RouterExecutionType): Promise<FlowExecutorContext> {
    return flowExecutor.execute({
        action: buildRouterWithOneCondition({
            children,
            conditions,
            executionType,
        }),
        executionState: FlowExecutorContext.empty(),
        constants: generateMockEngineConstants(),
    })
}
describe('router with branching different conditions', () => {
    it('should execute router with the first matching condition', async () => {
        const result = await executeRouterActionWithOneCondition([
            buildPieceAction({
                name: 'data_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 2 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'data_mapper_1',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 2 }}',
                    },
                },
            }),
        ], [
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'test',
                caseSensitive: false,
            },
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'anything',
                caseSensitive: false,
            },
        ], RouterExecutionType.EXECUTE_FIRST_MATCH)

        expect(result.verdict).toBe(ExecutionVerdict.RUNNING)
        expect(result.steps.data_mapper.output).toEqual({ 'key': 3 })
        expect(result.steps.data_mapper_1).toBeUndefined()
    })

    it('should execute router with the all matching conditions', async () => {
        const result = await executeRouterActionWithOneCondition([
            buildPieceAction({
                name: 'data_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 2 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'data_mapper_1',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 2 }}',
                    },
                },
            }),
        ], [
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'test',
                caseSensitive: false,
            },
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'test',
                caseSensitive: false,
            },
        ], RouterExecutionType.EXECUTE_ALL_MATCH)

        expect(result.verdict).toBe(ExecutionVerdict.RUNNING)
        expect(result.steps.data_mapper.output).toEqual({ 'key': 3 })
        expect(result.steps.data_mapper_1.output).toEqual({ 'key': 3 })
    })
    
    it('should execute router but no branch will match', async () => {
        const result = await executeRouterActionWithOneCondition([
            buildPieceAction({
                name: 'data_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 2 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'data_mapper_1',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 5 }}',
                    },
                },
            }),
        ], [
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'abc',
                secondValue: 'test',
                caseSensitive: false,
            },
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'fasc',
                caseSensitive: false,
            },
        ], RouterExecutionType.EXECUTE_ALL_MATCH)

        expect(result.verdict).toBe(ExecutionVerdict.RUNNING)
        const routerOutput = result.steps.router.output as { branches: boolean[] }
        expect(routerOutput.branches).toEqual([
            {
                branchName: 'Test Branch',
                branchIndex: 1,
                evaluation: false,
            },
            {
                branchName: 'Test Branch',
                branchIndex: 2,
                evaluation: false,
            },
        ])
        expect(result.steps.data_mapper).toBeUndefined()
        expect(result.steps.data_mapper_1).toBeUndefined()
    })

    it('should execute fallback branch with first match execution type', async () => {
        const result = await executeRouterActionWithOneCondition([
            buildPieceAction({
                name: 'data_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 2 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'data_mapper_1',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 5 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'fallback_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 10 }}',
                    },
                },
            }),
        ], [
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'abc',
                secondValue: 'test',
                caseSensitive: false,
            },
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'fasc',
                caseSensitive: false,
            },
            null, // Fallback branch
        ], RouterExecutionType.EXECUTE_FIRST_MATCH)

        expect(result.verdict).toBe(ExecutionVerdict.RUNNING)
        expect(result.steps.data_mapper).toBeUndefined()
        expect(result.steps.data_mapper_1).toBeUndefined()
        expect(result.steps.fallback_mapper.output).toEqual({ 'key': 11 })
    })

    it('should execute fallback branch with all match execution type', async () => {
        const result = await executeRouterActionWithOneCondition([
            buildPieceAction({
                name: 'data_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 2 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'data_mapper_1',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 5 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'fallback_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 10 }}',
                    },
                },
            }),
        ], [
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'abc',
                secondValue: 'test',
                caseSensitive: false,
            },
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'fasc',
                caseSensitive: false,
            },
            null, // Fallback branch
        ], RouterExecutionType.EXECUTE_ALL_MATCH)

        expect(result.verdict).toBe(ExecutionVerdict.RUNNING)
        expect(result.steps.data_mapper).toBeUndefined()
        expect(result.steps.data_mapper_1).toBeUndefined()
        expect(result.steps.fallback_mapper.output).toEqual({ 'key': 11 })
    })

    it('should not execute fallback branch when there is a matching condition in EXECUTE_FIRST_MATCH mode', async () => {
        const result = await executeRouterActionWithOneCondition([
            buildPieceAction({
                name: 'data_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 2 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'fallback_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 10 }}',
                    },
                },
            }),
        ], [
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'test',
                caseSensitive: false,
            },
            null, // Fallback branch
        ], RouterExecutionType.EXECUTE_FIRST_MATCH)

        expect(result.verdict).toBe(ExecutionVerdict.RUNNING)
        expect(result.steps.data_mapper.output).toEqual({ 'key': 3 })
        expect(result.steps.fallback_mapper).toBeUndefined()
    })

    it('should not execute fallback branch when there is a matching condition in EXECUTE_ALL_MATCH mode', async () => {
        const result = await executeRouterActionWithOneCondition([
            buildPieceAction({
                name: 'data_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 2 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'data_mapper_1',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 5 }}',
                    },
                },
            }),
            buildPieceAction({
                name: 'fallback_mapper',
                pieceName: '@activepieces/piece-data-mapper',
                actionName: 'advanced_mapping',
                input: {
                    mapping: {
                        'key': '{{ 1 + 10 }}',
                    },
                },
            }),
        ], [
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'test',
                caseSensitive: false,
            },
            {
                operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                firstValue: 'test',
                secondValue: 'test',
                caseSensitive: false,
            },
            null, // Fallback branch
        ], RouterExecutionType.EXECUTE_ALL_MATCH)

        expect(result.verdict).toBe(ExecutionVerdict.RUNNING)
        expect(result.steps.data_mapper.output).toEqual({ 'key': 3 })
        expect(result.steps.data_mapper_1.output).toEqual({ 'key': 6 })
        expect(result.steps.fallback_mapper).toBeUndefined()
    })
    it('should skip router', async () => {
        const result = await flowExecutor.execute({
            action: buildRouterWithOneCondition({ children: [
                buildPieceAction({
                    name: 'data_mapper',
                    skip: true,
                    pieceName: '@activepieces/piece-data-mapper',
                    actionName: 'advanced_mapping',
                    input: {},
                }),
            ], conditions: [
                {
                    operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                    firstValue: 'test',
                    secondValue: 'test',
                    caseSensitive: false,
                },
            ], executionType: RouterExecutionType.EXECUTE_FIRST_MATCH, skip: true }), executionState: FlowExecutorContext.empty(), constants: generateMockEngineConstants(),
        })
        expect(result.verdict).toBe(ExecutionVerdict.RUNNING)
        expect(result.steps.router).toBeUndefined()
    })
    it('should skip router action in flow', async () => {
        const router: Action = {
            ...buildRouterWithOneCondition({ children: [
                buildPieceAction({
                    name: 'data_mapper',
                    skip: true,
                    pieceName: '@activepieces/piece-data-mapper',
                    actionName: 'advanced_mapping',
                    input: {},
                }),
            ], conditions: [
                {
                    operator: BranchOperator.TEXT_EXACTLY_MATCHES,
                    firstValue: 'test',
                    secondValue: 'test',
                    caseSensitive: false,
                },
            ], 
            executionType: RouterExecutionType.EXECUTE_FIRST_MATCH, 
            skip: true }),
            nextAction: {
                ...buildCodeAction({
                    name: 'echo_step',
                    skip: false,
                    input: {
                        'key': '{{ 1 + 2 }}',
                    },
                }),
                nextAction: undefined,
            },
        }
        const result = await flowExecutor.execute({
            action: router, executionState: FlowExecutorContext.empty(), constants: generateMockEngineConstants(),
        })
        expect(result.verdict).toBe(ExecutionVerdict.RUNNING)
        expect(result.steps.router).toBeUndefined()
        expect(result.steps.echo_step.output).toEqual({ 'key': 3 })
    })
})
