import sys
import textwrap

import mpiunittest as unittest

from mpi4py import MPI

ModuleType = type(MPI)
ClassType = type(MPI.Comm)
FunctionType = type(MPI.Init)
StaticMethodType = type(MPI.buffer.allocate)
ClassMethodType = type(MPI.Comm.Get_parent)
MethodDescrType = type(MPI.Comm.Get_rank)
GetSetDescrType = type(MPI.Comm.rank)


def getdocstr(mc, docstrings, namespace=None):
    name = getattr(mc, "__name__", None)
    if name is None:
        return
    if name in ("__builtin__", "builtins"):
        return
    if namespace:
        name = f"{namespace}.{name}"
    if type(mc) in (
        ModuleType,
        ClassType,
    ):
        doc = getattr(mc, "__doc__", None)
        if doc == "<undocumented>":
            return
        docstrings[name] = doc
        for _k, v in vars(mc).items():
            if isinstance(v, (classmethod, staticmethod)):
                v = v.__get__(mc)
            getdocstr(v, docstrings, name)
    elif type(mc) in (
        FunctionType,
        StaticMethodType,
        ClassMethodType,
        MethodDescrType,
        GetSetDescrType,
    ):
        doc = getattr(mc, "__doc__", None)
        if doc == "<undocumented>":
            return
        if doc is not None:
            _, nl, doc = doc.partition("\n")
            if nl and doc.startswith("\n"):
                doc = textwrap.dedent(doc[1:])
        docstrings[name] = doc


@unittest.skipIf(hasattr(sys, "pypy_version_info"), "pypy")
class TestDoc(unittest.TestCase):
    #
    def testDoc(self):
        ignore = {"py2f", "f2py"}
        invalid = False
        missing = False
        docs = {}
        getdocstr(MPI, docs)
        for k in docs:
            doc = docs[k]
            name = k.split(".")[-1]
            if name in ignore:
                continue
            if not doc and name.startswith("_"):
                continue
            if doc is None:
                print(f"'{k}': missing docstring")
                missing = True
                continue
            if not doc.strip():
                print(f"'{k}': empty docstring")
                missing = True
                continue
            if doc.startswith("\n") and not doc.endswith(" "):
                print(f"'{k}': mismatch start and end whitespace")
                invalid = True
            if not doc.startswith("\n") and doc.endswith(" "):
                print(f"'{k}': mismatch start and end whitespace")
                invalid = True
            if doc.replace(" ", "").endswith("\n\n"):
                print(f"'{k}': docstring ends with too many newlines")
                invalid = True
            doc = doc.strip()
            if doc[0] == doc[0].lower():
                print(f"'{k}': docstring starts with lowercase")
                invalid = True
            if not doc.endswith("."):
                print(f"'{k}': docstring does not end with '.'")
                invalid = True
            summary, _, _ = doc.partition("\n")
            if not summary.endswith("."):
                print(f"'{k}': summary line does not end with '.'")
                invalid = True
        self.assertFalse(missing)
        self.assertFalse(invalid)


if __name__ == "__main__":
    unittest.main()
