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)

[((592, 641), 'fixit.ValidTestCase', 'Valid', (['"""assigned=\'string\'; f\'testing {assigned}\'"""'], {}), '("assigned=\'string\'; f\'testing {assigned}\'")\n', (597, 641), True, 'from fixit import ValidTestCase as Valid\n'), ((651, 675), 'fixit.ValidTestCase', 'Valid', (['"""\'simple string\'"""'], {}), '("\'simple string\'")\n', (656, 675), True, 'from fixit import ValidTestCase as Valid\n'), ((685, 719), 'fixit.ValidTestCase', 'Valid', (['"""\'concatenated\' + \'string\'"""'], {}), '("\'concatenated\' + \'string\'")\n', (690, 719), True, 'from fixit import ValidTestCase as Valid\n'), ((729, 776), 'fixit.ValidTestCase', 'Valid', (['"""b\'bytes %s\' % \'string\'.encode(\'utf-8\')"""'], {}), '("b\'bytes %s\' % \'string\'.encode(\'utf-8\')")\n', (734, 776), True, 'from fixit import ValidTestCase as Valid\n'), ((809, 854), 'fixit.InvalidTestCase', 'Invalid', (['"""\'hello, {name}\'.format(name=\'you\')"""'], {}), '("\'hello, {name}\'.format(name=\'you\')")\n', (816, 854), True, 'from fixit import InvalidTestCase as Invalid\n'), ((864, 894), 'fixit.InvalidTestCase', 'Invalid', (['"""\'hello, %s\' % \'you\'"""'], {}), '("\'hello, %s\' % \'you\'")\n', (871, 894), True, 'from fixit import InvalidTestCase as Invalid\n'), ((904, 943), 'fixit.InvalidTestCase', 'Invalid', (['"""r\'raw string value=%s\' % val"""'], {}), '("r\'raw string value=%s\' % val")\n', (911, 943), True, 'from fixit import InvalidTestCase as Invalid\n'), ((1604, 1648), 'libcst.ensure_type', 'cst.ensure_type', (['node.left', 'cst.SimpleString'], {}), '(node.left, cst.SimpleString)\n', (1619, 1648), True, 'import libcst as cst\n'), ((1358, 1374), 'libcst.matchers.SimpleString', 'm.SimpleString', ([], {}), '()\n', (1372, 1374), True, 'import libcst.matchers as m\n'), ((1385, 1395), 'libcst.matchers.Modulo', 'm.Modulo', ([], {}), '()\n', (1393, 1395), True, 'import libcst.matchers as m\n'), ((1101, 1117), 'libcst.matchers.SimpleString', 'm.SimpleString', ([], {}), '()\n', (1115, 1117), True, 'import libcst.matchers as m\n'), ((1124, 1146), 'libcst.matchers.Name', 'm.Name', ([], {'value': '"""format"""'}), "(value='format')\n", (1130, 1146), True, 'import libcst.matchers as m\n')]