from __future__ import annotations

import re

import pytest
from pre_commit.clientlib import load_manifest
from pre_commit.constants import MANIFEST_FILE

HOOKS = {h['id']: re.compile(h['entry']) for h in load_manifest(MANIFEST_FILE)}


@pytest.mark.parametrize(
    's',
    (
        'x = 1 # type: ignoreme',
        'x = 1  # type: int',
        'x = 1  # type int',
        'x = 1  # type: int  # noqa',
    ),
)
def test_python_use_type_annotations_positive(s):
    assert HOOKS['python-use-type-annotations'].search(s)


@pytest.mark.parametrize(
    's',
    (
        'x = 1',
        'x = 1  # type:ignore',
        'x = 1  # type: ignore',
        'x = 1  # type:  ignore',
        'x = 1  # type: ignore # noqa',
        'x = 1  # type: ignore  # noqa',
        'x = 1  # type: ignore[type-mismatch]',
        'x = 1  # type: ignore=E123',
    ),
)
def test_python_use_type_annotations_negative(s):
    assert not HOOKS['python-use-type-annotations'].search(s)


@pytest.mark.parametrize(
    's',
    (
        '# noqa',
        '# NOQA',
        '# noqa:F401',
        '# noqa:F401,W203',
    ),
)
def test_python_check_blanket_noqa_positive(s):
    assert HOOKS['python-check-blanket-noqa'].search(s)


@pytest.mark.parametrize(
    's',
    (
        'x = 1',
        '# noqa: F401',
        '# noqa: F401, W203',
    ),
)
def test_python_check_blanket_noqa_negative(s):
    assert not HOOKS['python-check-blanket-noqa'].search(s)


@pytest.mark.parametrize(
    's',
    (
        'x = 1  # type: ignore',
        'x = 1  # type ignore',
        'x = 1  # type:ignore',
        'x = 1  # type ignore  # noqa',
    ),
)
def test_python_check_blanket_type_ignore_positive(s):
    assert HOOKS['python-check-blanket-type-ignore'].search(s)


@pytest.mark.parametrize(
    's',
    (
        'x = 1',
        'x = 1  # type: ignore[attr-defined]',
        'x = 1  # type: ignore[attr-defined, name-defined]',
        'x = 1  # type: ignore[type-mismatch]  # noqa',
        'x = 1  # type: Union[int, str]',
        'x = 1  # type: ignoreme',
    ),
)
def test_python_check_blanket_type_ignore_negative(s):
    assert not HOOKS['python-check-blanket-type-ignore'].search(s)


@pytest.mark.parametrize(
    's',
    (
        'assert my_mock.not_called()',
        'assert my_mock.called_once_with()',
        'my_mock.assert_not_called',
        'my_mock.assert_called',
        'my_mock.assert_called_once_with',
        'my_mock.assert_called_once_with# noqa',
        'MyMock.assert_called_once_with',
    ),
)
def test_python_check_mock_methods_positive(s):
    assert HOOKS['python-check-mock-methods'].search(s)


@pytest.mark.parametrize(
    's',
    (
        'assert my_mock.call_count == 1',
        'assert my_mock.called',
        'my_mock.assert_not_called()',
        'my_mock.assert_called()',
        'my_mock.assert_called_once_with()',
        '"""like :meth:`Mock.assert_called_once_with`"""',
        '"""like :meth:`MagicMock.assert_called_once_with`"""',
    ),
)
def test_python_check_mock_methods_negative(s):
    assert not HOOKS['python-check-mock-methods'].search(s)


def test_python_noeval_positive():
    assert HOOKS['python-no-eval'].search('eval("3 + 4")')


def test_python_noeval_negative():
    assert not HOOKS['python-no-eval'].search('literal_eval("{1: 2}")')


@pytest.mark.parametrize(
    's',
    (
        'log.warn("this is deprecated")',
    ),
)
def test_python_no_log_warn_positive(s):
    assert HOOKS['python-no-log-warn'].search(s)


@pytest.mark.parametrize(
    's',
    (
        "warnings.warn('this is ok')",
        'log.warning("this is ok")',
        'from warnings import warn',
        'warn("by itself is also ok")',
    ),
)
def test_python_no_log_warn_negative(s):
    assert not HOOKS['python-no-log-warn'].search(s)


@pytest.mark.parametrize(
    's',
    (
        '`[code]`',
        'i like `_kitty`',
        'i like `_`',
        '`a`',
        '`cd`',
        ' `indented` literal block',
        '> quoted `literal` block',
    ),
)
def test_python_rst_backticks_positive(s):
    assert HOOKS['rst-backticks'].search(s)


@pytest.mark.parametrize(
    's',
    (
        ' ``[code]``',
        'i like _`kitty`',
        'i like `kitty`_',
        '``b``',
        '``ef``',
        '    indented `literal` block',
    ),
)
def test_python_rst_backticks_negative(s):
    assert not HOOKS['rst-backticks'].search(s)


@pytest.mark.parametrize(
    's',
    (
        '``PyMem_Realloc()`` indirectly call``PyObject_Malloc()`` and',
        'This PEP proposes that ``bytes`` and ``bytearray``gain an optimised',
        'Reading this we first see the``break``, which obviously applies to',
        'for using``long_description`` and a corresponding',
        '``inline`` normal``inline',
        '``inline``normal ``inline',
        '``inline``normal',
        '``inline``normal``inline',
        'normal ``inline``normal',
        'normal``inline`` normal',
        'normal``inline``',
        'normal``inline``normal',
    ),
)
def test_python_rst_inline_touching_normal_positive(s):
    assert HOOKS['rst-inline-touching-normal'].search(s)


@pytest.mark.parametrize(
    's',
    (
        '``PyMem_Realloc()`` indirectly call ``PyObject_Malloc()`` and',
        'This PEP proposes that ``bytes`` and ``bytearray`` gain an optimised',
        'Reading this we first see the ``break``, which obviously applies to',
        'for using ``long_description`` and a corresponding',
        '``inline`` normal ``inline',
        '``inline`` normal',
        'normal ``inline`` normal',
        'normal ``inline``',
    ),
)
def test_python_rst_inline_touching_normal_negative(s):
    assert not HOOKS['rst-inline-touching-normal'].search(s)


@pytest.mark.parametrize(
    's',
    (
        str(b'\x80abc', errors='replace'),
    ),
)
def test_text_unicode_replacement_char_positive(s):
    assert HOOKS['text-unicode-replacement-char'].search(s)


@pytest.mark.parametrize(
    's',
    (
        'foo',
    ),
)
def test_text_unicode_replacement_char_negative(s):
    assert not HOOKS['text-unicode-replacement-char'].search(s)


@pytest.mark.parametrize(
    's',
    (
        '    .. warning:',
        '.. warning:',
        '    .. warning ::',
        '.. warning ::',
        '    .. warning :',
        '.. warning :',
    ),
)
def test_rst_directive_colons_positive(s):
    assert HOOKS['rst-directive-colons'].search(s)


@pytest.mark.parametrize(
    's',
    (
        '.. warning::',
        '.. code:: python',
    ),
)
def test_rst_directive_colons_negative(s):
    assert not HOOKS['rst-directive-colons'].search(s)


def test_that_hooks_are_sorted():
    assert list(HOOKS) == sorted(HOOKS)
