"""Test for Python 3 string formatting error"""

# pylint: disable=too-few-public-methods, import-error, unused-argument, line-too-long,
# pylint: disable=consider-using-f-string
import os
import sys
import logging
from missing import Missing


class Custom:
    """ Has a __getattr__ """
    def __getattr__(self, _):
        return self


class Test:
    """ test format attribute access """
    custom = Custom()
    ids = [1, 2, 3, [4, 5, 6]]


class Getitem:
    """ test custom getitem for lookup access """
    def __getitem__(self, index):
        return 42


class ReturnYes:
    """ can't be properly inferred """
    missing = Missing()


def log(message, message_type="error"):
    """ Test """
    return message


def print_good():
    """ Good format strings """
    "{0} {1}".format(1, 2)
    "{0!r:20}".format("Hello")
    "{!r:20}".format("Hello")
    "{a!r:20}".format(a="Hello")
    "{pid}".format(pid=os.getpid())
    str("{}").format(2)
    "{0.missing.length}".format(ReturnYes())
    "{1.missing.length}".format(ReturnYes())
    "{a.ids[3][1]}".format(a=Test())
    "{a[0][0]}".format(a=[[1]])
    "{[0][0]}".format({0: {0: 1}})
    "{a.test}".format(a=Custom())
    "{a.__len__}".format(a=[])
    "{a.ids.__len__}".format(a=Test())
    "{a[0]}".format(a=Getitem())
    "{a[0][0]}".format(a=[Getitem()])
    "{[0][0]}".format(["test"])
    # these are skipped
    "{0} {1}".format(*[1, 2])
    "{a} {b}".format(**{'a': 1, 'b': 2})
    "{a}".format(a=Missing())
    logging.debug("%s", 42)
    logging.debug("%s %s", 42, 43)


def pprint_bad():
    """Test string format """
    "{{}}".format(1) # [format-string-without-interpolation]
    "{} {".format() # [bad-format-string]
    "{} }".format() # [bad-format-string]
    "{0} {}".format(1, 2) # [format-combined-specification]
    # +1: [missing-format-argument-key, unused-format-string-argument]
    "{a} {b}".format(a=1, c=2)
    "{} {a}".format(1, 2) # [missing-format-argument-key]
    "{} {}".format(1) # [too-few-format-args]
    "{} {}".format(1, 2, 3) # [too-many-format-args]
    # +1: [missing-format-argument-key,missing-format-argument-key,missing-format-argument-key]
    "{a} {b} {c}".format()
    "{} {}".format(a=1, b=2) # [too-few-format-args]
    # +1: [missing-format-argument-key, missing-format-argument-key]
    "{a} {b}".format(1, 2)
    "{0} {1} {a}".format(1, 2, 3) # [missing-format-argument-key]
    # +1: [missing-format-attribute]
    "{a.ids.__len__.length}".format(a=Test())
    "{a.ids[3][400]}".format(a=Test()) # [invalid-format-index]
    "{a.ids[3]['string']}".format(a=Test()) # [invalid-format-index]
    "{[0][1]}".format(["a"]) # [invalid-format-index]
    "{[0][0]}".format(((1, ))) # [invalid-format-index]
    # +1: [missing-format-argument-key, unused-format-string-argument]
    "{b[0]}".format(a=23)
    "{a[0]}".format(a=object) # [invalid-format-index]
    log("{}".format(2, "info")) # [too-many-format-args]
    "{0.missing}".format(2) # [missing-format-attribute]
    "{0} {1} {2}".format(1, 2) # [too-few-format-args]
    "{0} {1}".format(1, 2, 3) # [too-many-format-args]
    "{0} {a}".format(a=4) # [too-few-format-args]
    "{[0]} {}".format([4]) # [too-few-format-args]
    "{[0]} {}".format([4], 5, 6) # [too-many-format-args]
    logging.debug("%s %s", 42) # [logging-too-few-args]
    logging.debug("%s", 42, 43) # [logging-too-many-args]
    "String".format(1)  # [format-string-without-interpolation]
    "String".format(())  # [format-string-without-interpolation]
    "String".format([])  # [format-string-without-interpolation]
    "String".format(None)  # [format-string-without-interpolation]


def good_issue288(*args, **kwargs):
    """ Test that using kwargs does not emit a false
    positive.
    """
    'Hello John Doe {0[0]}'.format(args)
    'Hello {0[name]}'.format(kwargs)


def good_issue287():
    """ Test that the string format checker skips
    format nodes which don't have a string as a parent
    (but a subscript, name etc).
    """
    name = 'qwerty'
    ret = {'comment': ''}
    ret['comment'] = 'MySQL grant {0} is set to be revoked'
    ret['comment'] = ret['comment'].format(name)
    return ret, name


def nested_issue294():
    """ Test nested format fields. """
    '{0:>{1}}'.format(42, 24)
    '{0:{a[1]}} {a}'.format(1, a=[1, 2])
    '{:>{}}'.format(42, 24)
    '{0:>{1}}'.format(42) # [too-few-format-args]
    '{0:>{1}}'.format(42, 24, 54) # [too-many-format-args]
    '{0:{a[1]}}'.format(1) # [missing-format-argument-key]
    '{0:{a.x}}'.format(1, a=2) # [missing-format-attribute]


def issue310():
    """ Test a regression using duplicate manual position arguments. """
    '{0} {1} {0}'.format(1, 2)
    '{0} {1} {0}'.format(1) # [too-few-format-args]


def issue322():
    """ Test a regression using mixed manual position arguments
    and attribute access arguments.
    """
    '{0}{1[FOO]}'.format(123, {'FOO': 456})
    '{0}{1[FOO]}'.format(123, {'FOO': 456}, 321) # [too-many-format-args]
    '{0}{1[FOO]}'.format(123) # [too-few-format-args]


def issue338():
    """
    Check that using a namedtuple subclass doesn't crash when
    trying to infer EmptyNodes (resulted after mocking the
    members of namedtuples).
    """
    from collections import namedtuple # pylint: disable=import-outside-toplevel

    class Crash(namedtuple("C", "foo bar")):
        """ Looking for attributes in __str__ will crash,
        because EmptyNodes can't be inferred.
        """
        def __str__(self):
            return "{0.foo}: {0.bar}".format(self)
    return Crash


def issue351():
    """
    Check that the format method can be assigned to a variable, ie:
    """
    fmt = 'test {} {}'.format
    fmt('arg1') # [too-few-format-args]
    fmt('arg1', 'arg2')
    fmt('arg1', 'arg2', 'arg3') # [too-many-format-args]


def issue373():
    """
    Ignore any object coming from an argument.
    """
    class SomeClass:
        """ empty docstring. """
        def __init__(self, opts=None):
            self.opts = opts

        def dunc(self, arg):
            """Don't try to analyze this."""
            return "A{0}{1}".format(arg, self.opts)

        def func(self):
            """Don't try to analyze the following string."""
            return 'AAA{0[iface]}BBB{0[port]}'.format(self.opts)

    return SomeClass


def issue_463():
    """
    Mix positional arguments, `{0}`, with positional
    arguments with attribute access, `{0.__x__}`.
    """
    data = "{0.__class__.__name__}: {0}".format(42)
    data2 = "{0[0]}: {0}".format([1])
    return (data, data2)


def avoid_empty_attribute():
    """The following string is invalid, avoid crashing."""

    return "There are {.:2f} undiscovered errors.".format(1) # [bad-format-string]


def invalid_format_index_on_inference_ambiguity():
    """Test inference bug for invalid-format-index"""
    if len(sys.argv) > 1:
        options = [["Woof!"]]
    else:
        options = [["Barf!"]]
    return 'Why is this bad? {options[0][0]}'.format(options=options)
