from __future__ import annotations

import ast

import pytest

from pre_commit_hooks.check_builtin_literals import Call
from pre_commit_hooks.check_builtin_literals import main
from pre_commit_hooks.check_builtin_literals import Visitor

BUILTIN_CONSTRUCTORS = '''\
import builtins

c1 = complex()
d1 = dict()
f1 = float()
i1 = int()
l1 = list()
s1 = str()
t1 = tuple()

c2 = builtins.complex()
d2 = builtins.dict()
f2 = builtins.float()
i2 = builtins.int()
l2 = builtins.list()
s2 = builtins.str()
t2 = builtins.tuple()
'''
BUILTIN_LITERALS = '''\
c1 = 0j
d1 = {}
f1 = 0.0
i1 = 0
l1 = []
s1 = ''
t1 = ()
'''


@pytest.fixture
def visitor():
    return Visitor()


@pytest.mark.parametrize(
    ('expression', 'calls'),
    [
        # see #285
        ('x[0]()', []),
        # complex
        ('0j', []),
        ('complex()', [Call('complex', 1, 0)]),
        ('complex(0, 0)', []),
        ("complex('0+0j')", []),
        ('builtins.complex()', []),
        # float
        ('0.0', []),
        ('float()', [Call('float', 1, 0)]),
        ("float('0.0')", []),
        ('builtins.float()', []),
        # int
        ('0', []),
        ('int()', [Call('int', 1, 0)]),
        ("int('0')", []),
        ('builtins.int()', []),
        # list
        ('[]', []),
        ('list()', [Call('list', 1, 0)]),
        ("list('abc')", []),
        ("list([c for c in 'abc'])", []),
        ("list(c for c in 'abc')", []),
        ('builtins.list()', []),
        # str
        ("''", []),
        ('str()', [Call('str', 1, 0)]),
        ("str('0')", []),
        ('builtins.str()', []),
        # tuple
        ('()', []),
        ('tuple()', [Call('tuple', 1, 0)]),
        ("tuple('abc')", []),
        ("tuple([c for c in 'abc'])", []),
        ("tuple(c for c in 'abc')", []),
        ('builtins.tuple()', []),
    ],
)
def test_non_dict_exprs(visitor, expression, calls):
    visitor.visit(ast.parse(expression))
    assert visitor.builtin_type_calls == calls


@pytest.mark.parametrize(
    ('expression', 'calls'),
    [
        ('{}', []),
        ('dict()', [Call('dict', 1, 0)]),
        ('dict(a=1, b=2, c=3)', []),
        ("dict(**{'a': 1, 'b': 2, 'c': 3})", []),
        ("dict([(k, v) for k, v in [('a', 1), ('b', 2), ('c', 3)]])", []),
        ("dict((k, v) for k, v in [('a', 1), ('b', 2), ('c', 3)])", []),
        ('builtins.dict()', []),
    ],
)
def test_dict_allow_kwargs_exprs(visitor, expression, calls):
    visitor.visit(ast.parse(expression))
    assert visitor.builtin_type_calls == calls


@pytest.mark.parametrize(
    ('expression', 'calls'),
    [
        ('dict()', [Call('dict', 1, 0)]),
        ('dict(a=1, b=2, c=3)', [Call('dict', 1, 0)]),
        ("dict(**{'a': 1, 'b': 2, 'c': 3})", [Call('dict', 1, 0)]),
        ('builtins.dict()', []),
    ],
)
def test_dict_no_allow_kwargs_exprs(expression, calls):
    visitor = Visitor(allow_dict_kwargs=False)
    visitor.visit(ast.parse(expression))
    assert visitor.builtin_type_calls == calls


def test_ignore_constructors():
    visitor = Visitor(
        ignore=('complex', 'dict', 'float', 'int', 'list', 'str', 'tuple'),
    )
    visitor.visit(ast.parse(BUILTIN_CONSTRUCTORS))
    assert visitor.builtin_type_calls == []


def test_failing_file(tmpdir):
    f = tmpdir.join('f.py')
    f.write(BUILTIN_CONSTRUCTORS)
    rc = main([str(f)])
    assert rc == 1


def test_passing_file(tmpdir):
    f = tmpdir.join('f.py')
    f.write(BUILTIN_LITERALS)
    rc = main([str(f)])
    assert rc == 0


def test_failing_file_ignore_all(tmpdir):
    f = tmpdir.join('f.py')
    f.write(BUILTIN_CONSTRUCTORS)
    rc = main(['--ignore=complex,dict,float,int,list,str,tuple', str(f)])
    assert rc == 0
