import libcst as cst
import libcst.matchers as m
from fixit import CstLintRule
from fixit import InvalidTestCase as Invalid
from fixit import ValidTestCase as Valid


class UseFstringRule(CstLintRule):

    MESSAGE: str = (
        "As mentioned in the [Contributing Guidelines]"
        + "(https://github.com/TheAlgorithms/Python/blob/master/CONTRIBUTING.md), "
        + "please do not use printf style formatting or `str.format()`. "
        + "Use [f-string](https://realpython.com/python-f-strings/) instead to be "
        + "more readable and efficient."
    )

    VALID = [
        Valid("assigned='string'; f'testing {assigned}'"),
        Valid("'simple string'"),
        Valid("'concatenated' + 'string'"),
        Valid("b'bytes %s' % 'string'.encode('utf-8')"),
    ]

    INVALID = [
        Invalid("'hello, {name}'.format(name='you')"),
        Invalid("'hello, %s' % 'you'"),
        Invalid("r'raw string value=%s' % val"),
    ]

    def visit_Call(self, node: cst.Call) -> None:
        if m.matches(
            node,
            m.Call(
                func=m.Attribute(value=m.SimpleString(), attr=m.Name(value="format"))
            ),
        ):
            self.report(node)

    def visit_BinaryOperation(self, node: cst.BinaryOperation) -> None:
        if (
            m.matches(
                node, m.BinaryOperation(left=m.SimpleString(), operator=m.Modulo())
            )
            # SimpleString can be bytes and fstring don't support bytes.
            # https://www.python.org/dev/peps/pep-0498/#no-binary-f-strings
            and isinstance(
                cst.ensure_type(node.left, cst.SimpleString).evaluated_value, str
            )
        ):
            self.report(node)

[((19, 8, 19, 57), 'fixit.ValidTestCase', 'Valid', ({(19, 14, 19, 56): '"""assigned=\'string\'; f\'testing {assigned}\'"""'}, {}), '("assigned=\'string\'; f\'testing {assigned}\'")', True, 'from fixit import ValidTestCase as Valid\n'), ((20, 8, 20, 32), 'fixit.ValidTestCase', 'Valid', ({(20, 14, 20, 31): '"""\'simple string\'"""'}, {}), '("\'simple string\'")', True, 'from fixit import ValidTestCase as Valid\n'), ((21, 8, 21, 42), 'fixit.ValidTestCase', 'Valid', ({(21, 14, 21, 41): '"""\'concatenated\' + \'string\'"""'}, {}), '("\'concatenated\' + \'string\'")', True, 'from fixit import ValidTestCase as Valid\n'), ((22, 8, 22, 55), 'fixit.ValidTestCase', 'Valid', ({(22, 14, 22, 54): '"""b\'bytes %s\' % \'string\'.encode(\'utf-8\')"""'}, {}), '("b\'bytes %s\' % \'string\'.encode(\'utf-8\')")', True, 'from fixit import ValidTestCase as Valid\n'), ((26, 8, 26, 53), 'fixit.InvalidTestCase', 'Invalid', ({(26, 16, 26, 52): '"""\'hello, {name}\'.format(name=\'you\')"""'}, {}), '("\'hello, {name}\'.format(name=\'you\')")', True, 'from fixit import InvalidTestCase as Invalid\n'), ((27, 8, 27, 38), 'fixit.InvalidTestCase', 'Invalid', ({(27, 16, 27, 37): '"""\'hello, %s\' % \'you\'"""'}, {}), '("\'hello, %s\' % \'you\'")', True, 'from fixit import InvalidTestCase as Invalid\n'), ((28, 8, 28, 47), 'fixit.InvalidTestCase', 'Invalid', ({(28, 16, 28, 46): '"""r\'raw string value=%s\' % val"""'}, {}), '("r\'raw string value=%s\' % val")', True, 'from fixit import InvalidTestCase as Invalid\n'), ((48, 16, 48, 60), 'libcst.ensure_type', 'cst.ensure_type', ({(48, 32, 48, 41): 'node.left', (48, 43, 48, 59): 'cst.SimpleString'}, {}), '(node.left, cst.SimpleString)', True, 'import libcst as cst\n'), ((43, 45, 43, 61), 'libcst.matchers.SimpleString', 'm.SimpleString', ({}, {}), '()', True, 'import libcst.matchers as m\n'), ((43, 72, 43, 82), 'libcst.matchers.Modulo', 'm.Modulo', ({}, {}), '()', True, 'import libcst.matchers as m\n'), ((35, 39, 35, 55), 'libcst.matchers.SimpleString', 'm.SimpleString', ({}, {}), '()', True, 'import libcst.matchers as m\n'), ((35, 62, 35, 84), 'libcst.matchers.Name', 'm.Name', (), '', True, 'import libcst.matchers as m\n')]