# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import ast

import pytest

from authentic2.utils.evaluate import (
    BaseExpressionValidator,
    ConditionValidator,
    ExpressionError,
    HTTPHeaders,
    evaluate_condition,
)


def test_base():
    v = BaseExpressionValidator()

    #    assert v('1')[0] is False
    #    assert v('\'a\'')[0] is False
    #    assert v('x')[0] is False

    v = BaseExpressionValidator(authorized_nodes=[ast.Num, ast.Str])

    assert v('1')
    assert v('\'a\'')

    # code object is cached
    assert v('1') is v('1')
    assert v('\'a\'') is v('\'a\'')
    with pytest.raises(ExpressionError):
        assert v('x')


def test_condition_validator():
    v = ConditionValidator()
    assert v('x < 2 and y == \'u\' or \'a\' in z')
    with pytest.raises(ExpressionError) as raised:
        v('a and _b')
    assert raised.value.code == 'invalid-variable'
    assert raised.value.text == '_b'

    with pytest.raises(ExpressionError) as raised:
        v('a + b')
    assert str(raised.value) == '[\'expression "a + b" is forbidden\']'

    with pytest.raises(ExpressionError) as raised:
        v('1 + 2')

    v('a[1]')

    v('a[\'xx\']')

    with pytest.raises(ExpressionError, match='MUST be a constant'):
        v('a[1:2]')

    with pytest.raises(ExpressionError, match='MUST be a constant'):
        v('headers[headers]')


def test_evaluate_condition(rf):
    assert evaluate_condition('False') is False
    assert evaluate_condition('True') is True
    assert evaluate_condition('True and False') is False
    assert evaluate_condition('True or False') is True
    assert evaluate_condition('a or b', ctx=dict(a=True, b=False)) is True
    assert evaluate_condition('a < 1', ctx=dict(a=0)) is True
    with pytest.raises(ExpressionError) as exc_info:
        evaluate_condition('a < 1')
    assert exc_info.value.code == 'undefined-variable'
    assert evaluate_condition('a < 1', on_raise=False) is False


def test_http_headers(rf):
    request = rf.get('/', HTTP_X_ENTROUVERT='1')
    headers = HTTPHeaders(request)
    assert evaluate_condition('"X-Entrouvert" in headers', ctx={'headers': headers}) is True
    assert evaluate_condition('headers["X-Entrouvert"]', ctx={'headers': headers}) == '1'
