<?php

app_require('modules/selfserve/classes/selfserve_condition_evaluator.php');

use modules\selfserve\classes\selfserve_condition_evaluator;
use modules\selfserve\helpers\selfserve_condition_rule_object_type;
use modules\selfserve\helpers\selfserve_condition_rule_type;
use modules\selfserve\helpers\selfserve_task_gate_type;

it('evaluates self-serve conditions with combined AND and OR semantics', function (): void {
    $evaluator = new selfserve_condition_evaluator();

    $conditions = [
        ['id' => 10],
        ['id' => 20],
    ];

    $rules = [
        [
            'condition_id' => 10,
            'type' => selfserve_condition_rule_type::IS_TRUE->value,
            'object_type' => selfserve_condition_rule_object_type::QUESTION->value,
            'object_id' => 1,
        ],
        [
            'condition_id' => 10,
            'type' => selfserve_condition_rule_type::IS_FALSE_OR_NOT_SET->value,
            'object_type' => selfserve_condition_rule_object_type::QUESTION->value,
            'object_id' => 2,
        ],
        [
            'condition_id' => 20,
            'type' => selfserve_condition_rule_type::IS_TRUE->value,
            'object_type' => selfserve_condition_rule_object_type::QUESTION->value,
            'object_id' => 5,
        ],
        [
            'condition_id' => 20,
            'type' => selfserve_condition_rule_type::IS_TRUE_OR_ANY_TRUE->value,
            'object_type' => selfserve_condition_rule_object_type::QUESTION->value,
            'object_id' => 3,
        ],
        [
            'condition_id' => 20,
            'type' => selfserve_condition_rule_type::IS_TRUE_OR_ANY_TRUE->value,
            'object_type' => selfserve_condition_rule_object_type::QUESTION->value,
            'object_id' => 4,
        ],
    ];

    $answers = [
        1 => true,
        3 => false,
        4 => true,
        5 => true,
    ];

    expect($evaluator->evaluate($conditions, $rules, $answers))->toBe([
        10 => true,
        20 => true,
    ]);
});

it('prefers condition results over question answers when evaluating task gates', function (): void {
    $evaluator = new selfserve_condition_evaluator();

    expect($evaluator->taskGateSatisfied(14, [14 => false], [14 => true]))->toBeFalse();
    expect($evaluator->taskGateSatisfied(15, [], [15 => true]))->toBeTrue();
    expect($evaluator->taskGateSatisfied(null, [], []))->toBeTrue();
});

it('evaluates typed task gates with strict semantics', function (): void {
    $evaluator = new selfserve_condition_evaluator();

    expect($evaluator->taskGateSatisfiedTyped(selfserve_task_gate_type::ALWAYS->value, null, [], []))->toBeTrue();
    expect($evaluator->taskGateSatisfiedTyped(selfserve_task_gate_type::CONDITION->value, 31, [31 => true], [31 => false]))->toBeTrue();
    expect($evaluator->taskGateSatisfiedTyped(selfserve_task_gate_type::CONDITION->value, 31, [31 => false], [31 => true]))->toBeFalse();
    expect($evaluator->taskGateSatisfiedTyped(selfserve_task_gate_type::QUESTION->value, 8, [], [8 => true]))->toBeTrue();
    expect($evaluator->taskGateSatisfiedTyped(selfserve_task_gate_type::QUESTION->value, 8, [8 => true], [8 => false]))->toBeFalse();
});

it('evaluates nested v2 ALL and ANY expression trees with trace output', function (): void {
    $evaluator = new selfserve_condition_evaluator();

    $conditions = [
        [
            'id' => 10,
            'expression' => [
                'type' => 'group',
                'operator' => 'ALL',
                'children' => [
                    ['type' => 'predicate', 'subject_type' => 'question', 'subject_id' => 1, 'operator' => 'IS_TRUE'],
                    [
                        'type' => 'group',
                        'operator' => 'ANY',
                        'children' => [
                            ['type' => 'predicate', 'subject_type' => 'question', 'subject_id' => 2, 'operator' => 'IS_TRUE'],
                            ['type' => 'predicate', 'subject_type' => 'question', 'subject_id' => 3, 'operator' => 'IS_FALSE_OR_NOT_SET'],
                        ],
                    ],
                ],
            ],
        ],
        [
            'id' => 20,
            'expression' => [
                'type' => 'group',
                'operator' => 'ALL',
                'children' => [
                    ['type' => 'predicate', 'subject_type' => 'condition', 'subject_id' => 10, 'operator' => 'IS_TRUE'],
                    ['type' => 'predicate', 'subject_type' => 'question', 'subject_id' => 4, 'operator' => 'IS_SET'],
                ],
            ],
        ],
    ];

    $evaluation = $evaluator->evaluateExpressionsWithTrace($conditions, [
        1 => true,
        2 => false,
        4 => false,
    ]);

    expect($evaluation['results'])->toBe([
        10 => true,
        20 => true,
    ]);
    expect($evaluation['trace'][10]['expression']['children'][1]['operator'])->toBe('ANY');
    expect($evaluation['trace'][20]['expression']['children'][0]['subject_type'])->toBe('condition');
});

it('evaluates v2 if, else if, and else condition branches in order', function (): void {
    $evaluator = new selfserve_condition_evaluator();

    $evaluation = $evaluator->evaluateExpressionsWithTrace([
        [
            'id' => 10,
            'expression' => [
                'type' => 'branch',
                'branches' => [
                    [
                        'kind' => 'if',
                        'when' => [
                            'type' => 'predicate',
                            'subject_type' => 'question',
                            'subject_id' => 1,
                            'operator' => 'IS_TRUE',
                        ],
                        'then' => [
                            'type' => 'predicate',
                            'subject_type' => 'question',
                            'subject_id' => 2,
                            'operator' => 'IS_TRUE',
                        ],
                    ],
                    [
                        'kind' => 'else_if',
                        'when' => [
                            'type' => 'predicate',
                            'subject_type' => 'question',
                            'subject_id' => 3,
                            'operator' => 'IS_TRUE',
                        ],
                        'then' => [
                            'type' => 'predicate',
                            'subject_type' => 'question',
                            'subject_id' => 4,
                            'operator' => 'IS_FALSE',
                        ],
                    ],
                    [
                        'kind' => 'else',
                        'else' => true,
                        'then' => [
                            'type' => 'predicate',
                            'subject_type' => 'question',
                            'subject_id' => 5,
                            'operator' => 'IS_TRUE',
                        ],
                    ],
                ],
            ],
        ],
    ], [
        1 => false,
        3 => true,
        4 => false,
        5 => false,
    ]);

    expect($evaluation['results'][10])->toBeTrue();
    expect($evaluation['trace'][10]['expression']['selected_index'])->toBe(1);
    expect($evaluation['trace'][10]['expression']['branches'][1]['kind'])->toBe('else_if');
});

it('evaluates v2 case expressions against question values', function (): void {
    $evaluator = new selfserve_condition_evaluator();

    $evaluation = $evaluator->evaluateExpressionsWithTrace([
        [
            'id' => 10,
            'expression' => [
                'type' => 'case',
                'subject_type' => 'question',
                'subject_id' => 1,
                'cases' => [
                    [
                        'value' => true,
                        'then' => [
                            'type' => 'predicate',
                            'subject_type' => 'question',
                            'subject_id' => 2,
                            'operator' => 'IS_TRUE',
                        ],
                    ],
                    [
                        'value' => false,
                        'then' => [
                            'type' => 'predicate',
                            'subject_type' => 'question',
                            'subject_id' => 3,
                            'operator' => 'IS_FALSE',
                        ],
                    ],
                ],
            ],
        ],
    ], [
        1 => false,
        3 => false,
    ]);

    expect($evaluation['results'][10])->toBeTrue();
    expect($evaluation['trace'][10]['expression']['selected_index'])->toBe(1);
    expect($evaluation['trace'][10]['expression']['actual_value'])->toBeFalse();
});

it('returns false and traces v2 condition expression cycles', function (): void {
    $evaluator = new selfserve_condition_evaluator();

    $evaluation = $evaluator->evaluateExpressionsWithTrace([
        [
            'id' => 10,
            'expression' => [
                'type' => 'group',
                'operator' => 'ALL',
                'children' => [
                    ['type' => 'predicate', 'subject_type' => 'condition', 'subject_id' => 20, 'operator' => 'IS_TRUE'],
                ],
            ],
        ],
        [
            'id' => 20,
            'expression' => [
                'type' => 'group',
                'operator' => 'ALL',
                'children' => [
                    ['type' => 'predicate', 'subject_type' => 'condition', 'subject_id' => 10, 'operator' => 'IS_TRUE'],
                ],
            ],
        ],
    ], []);

    expect($evaluation['results'][10])->toBeFalse();
    expect($evaluation['results'][20])->toBeFalse();
    expect(json_encode($evaluation['trace']))->toContain('Condition dependency cycle detected');
});
