""" interactive debugging with PDB, the Python Debugger. """
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import pdb
import sys
from doctest import UnexpectedException

from _pytest import outcomes
from _pytest.config import hookimpl


def _validate_usepdb_cls(value):
    try:
        modname, classname = value.split(":")
    except ValueError:
        raise argparse.ArgumentTypeError(
            "{!r} is not in the format 'modname:classname'".format(value)
        )

    try:
        __import__(modname)
        mod = sys.modules[modname]

        # Handle --pdbcls=pdb:pdb.Pdb (useful e.g. with pdbpp).
        parts = classname.split(".")
        pdb_cls = getattr(mod, parts[0])
        for part in parts[1:]:
            pdb_cls = getattr(pdb_cls, part)

        return pdb_cls
    except Exception as exc:
        raise argparse.ArgumentTypeError(
            "could not get pdb class for {!r}: {}".format(value, exc)
        )


def pytest_addoption(parser):
    group = parser.getgroup("general")
    group._addoption(
        "--pdb",
        dest="usepdb",
        action="store_true",
        help="start the interactive Python debugger on errors or KeyboardInterrupt.",
    )
    group._addoption(
        "--pdbcls",
        dest="usepdb_cls",
        metavar="modulename:classname",
        type=_validate_usepdb_cls,
        help="start a custom interactive Python debugger on errors. "
        "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb",
    )
    group._addoption(
        "--trace",
        dest="trace",
        action="store_true",
        help="Immediately break when running each test.",
    )


def pytest_configure(config):
    pdb_cls = config.getvalue("usepdb_cls")
    if not pdb_cls:
        pdb_cls = pdb.Pdb

    if config.getvalue("trace"):
        config.pluginmanager.register(PdbTrace(), "pdbtrace")
    if config.getvalue("usepdb"):
        config.pluginmanager.register(PdbInvoke(), "pdbinvoke")

    pytestPDB._saved.append(
        (pdb.set_trace, pytestPDB._pluginmanager, pytestPDB._config, pytestPDB._pdb_cls)
    )
    pdb.set_trace = pytestPDB.set_trace
    pytestPDB._pluginmanager = config.pluginmanager
    pytestPDB._config = config
    pytestPDB._pdb_cls = pdb_cls

    # NOTE: not using pytest_unconfigure, since it might get called although
    #       pytest_configure was not (if another plugin raises UsageError).
    def fin():
        (
            pdb.set_trace,
            pytestPDB._pluginmanager,
            pytestPDB._config,
            pytestPDB._pdb_cls,
        ) = pytestPDB._saved.pop()

    config._cleanup.append(fin)


class pytestPDB(object):
    """ Pseudo PDB that defers to the real pdb. """