"""Tests for :mod:`sphinx.ext.napoleon.__init__` module."""

import sys
from collections import namedtuple
from unittest import TestCase, mock

from sphinx.application import Sphinx
from sphinx.ext.napoleon import Config, _process_docstring, _skip_member, setup
from sphinx.testing.util import simple_decorator


def _private_doc():
    """module._private_doc.DOCSTRING"""
    pass


def _private_undoc():
    pass


def __special_doc__():
    """module.__special_doc__.DOCSTRING"""
    pass


def __special_undoc__():
    pass


class SampleClass:
    def _private_doc(self):
        """SampleClass._private_doc.DOCSTRING"""
        pass

    def _private_undoc(self):
        pass

    def __special_doc__(self):
        """SampleClass.__special_doc__.DOCSTRING"""
        pass

    def __special_undoc__(self):
        pass

    @simple_decorator
    def __decorated_func__(self):
        """doc"""
        pass


class SampleError(Exception):
    def _private_doc(self):
        """SampleError._private_doc.DOCSTRING"""
        pass

    def _private_undoc(self):
        pass

    def __special_doc__(self):
        """SampleError.__special_doc__.DOCSTRING"""
        pass

    def __special_undoc__(self):
        pass


SampleNamedTuple = namedtuple('SampleNamedTuple', 'user_id block_type def_id')


class ProcessDocstringTest(TestCase):
    def test_modify_in_place(self):
        lines = ['Summary line.',
                 '',
                 'Args:',
                 '   arg1: arg1 description']
        app = mock.Mock()
        app.config = Config()
        _process_docstring(app, 'class', 'SampleClass', SampleClass,
                           mock.Mock(), lines)

        expected = ['Summary line.',
                    '',
                    ':param arg1: arg1 description',
                    '']
        self.assertEqual(expected, lines)


class SetupTest(TestCase):
    def test_unknown_app_type(self):
        setup(object())

    def test_add_config_values(self):
        app = mock.Mock(Sphinx)
        setup(app)
        for name in Config._config_values:
            has_config = False
            for method_name, args, _kwargs in app.method_calls:
                if(method_name == 'add_config_value' and
                   args[0] == name):
                    has_config = True
            if not has_config:
                self.fail('Config value was not added to app %s' % name)

        has_process_docstring = False
        has_skip_member = False
        for method_name, args, _kwargs in app.method_calls:
            if method_name == 'connect':
                if(args[0] == 'autodoc-process-docstring' and
                   args[1] == _process_docstring):
                    has_process_docstring = True
                elif(args[0] == 'autodoc-skip-member' and
                     args[1] == _skip_member):
                    has_skip_member = True
        if not has_process_docstring:
            self.fail('autodoc-process-docstring never connected')
        if not has_skip_member:
            self.fail('autodoc-skip-member never connected')


class SkipMemberTest(TestCase):
    def assertSkip(self, what, member, obj, expect_default_skip, config_name):
        skip = True
        app = mock.Mock()
        app.config = Config()
        setattr(app.config, config_name, True)
        if expect_default_skip:
            self.assertEqual(None, _skip_member(app, what, member, obj, skip,
                                                mock.Mock()))
        else:
            self.assertIs(_skip_member(app, what, member, obj, skip,
                                       mock.Mock()), False)
        setattr(app.config, config_name, False)
        self.assertEqual(None, _skip_member(app, what, member, obj, skip,
                                            mock.Mock()))

    def test_namedtuple(self):
        if sys.version_info < (3, 7):
            self.assertSkip('class', '_asdict',
                            SampleNamedTuple._asdict, False,
                            'napoleon_include_private_with_doc')
        else:
            # Since python 3.7, namedtuple._asdict() has not been documented
            # because there is no way to check the method is a member of the
            # namedtuple class.  This testcase confirms only it does not
            # raise an error on building document (refs: #1455)
            self.assertSkip('class', '_asdict',
                            SampleNamedTuple._asdict, True,
                            'napoleon_include_private_with_doc')

    def test_class_private_doc(self):
        self.assertSkip('class', '_private_doc',
                        SampleClass._private_doc, False,
                        'napoleon_include_private_with_doc')

    def test_class_private_undoc(self):
        self.assertSkip('class', '_private_undoc',
                        SampleClass._private_undoc, True,
                        'napoleon_include_private_with_doc')

    def test_class_special_doc(self):
        self.assertSkip('class', '__special_doc__',
                        SampleClass.__special_doc__, False,
                        'napoleon_include_special_with_doc')

    def test_class_special_undoc(self):
        self.assertSkip('class', '__special_undoc__',
                        SampleClass.__special_undoc__, True,
                        'napoleon_include_special_with_doc')

    def test_class_decorated_doc(self):
        self.assertSkip('class', '__decorated_func__',
                        SampleClass.__decorated_func__, False,
                        'napoleon_include_special_with_doc')

    def test_exception_private_doc(self):
        self.assertSkip('exception', '_private_doc',
                        SampleError._private_doc, False,
                        'napoleon_include_private_with_doc')

    def test_exception_private_undoc(self):
        self.assertSkip('exception', '_private_undoc',
                        SampleError._private_undoc, True,
                        'napoleon_include_private_with_doc')

    def test_exception_special_doc(self):
        self.assertSkip('exception', '__special_doc__',
                        SampleError.__special_doc__, False,
                        'napoleon_include_special_with_doc')

    def test_exception_special_undoc(self):
        self.assertSkip('exception', '__special_undoc__',
                        SampleError.__special_undoc__, True,
                        'napoleon_include_special_with_doc')

    def test_module_private_doc(self):
        self.assertSkip('module', '_private_doc', _private_doc, False,
                        'napoleon_include_private_with_doc')

    def test_module_private_undoc(self):
        self.assertSkip('module', '_private_undoc', _private_undoc, True,
                        'napoleon_include_private_with_doc')

    def test_module_special_doc(self):
        self.assertSkip('module', '__special_doc__', __special_doc__, False,
                        'napoleon_include_special_with_doc')

    def test_module_special_undoc(self):
        self.assertSkip('module', '__special_undoc__', __special_undoc__, True,
                        'napoleon_include_special_with_doc')
