import re
from pylint.checkers import BaseChecker
from pylint.lint import PyLinter
from pylint.reporters.text import TextReporter


class DocstringLanguageChecker(BaseChecker):
    name = "docstring-language-checker"
    priority = -1

    msgs = {
        "E9001": (
            "Function '%s' has more than %d lines and no docstring",
            "missing-docstring-for-long-function",
            "Used when a long function has more than N lines but no docstring.",
        ),
        "E9002": (
            "Function '%s' has a docstring written in Vietnamese",
            "vietnamese-docstring-detected",
            "Used when a docstring contains Vietnamese characters.",
        ),
        "E9003": (
            "Function '%s' docstring missing Args section (Google Style)",
            "missing-args-section",
            "Used when a function with parameters is missing an Args section.",
        ),
        "E9004": (
            "Function '%s' docstring missing Returns section (Google Style)",
            "missing-returns-section",
            "Used when a function that returns a value is missing a Returns section.",
        ),
        "E9005": (
            "Function '%s' docstring missing Raises section (Google Style)",
            "missing-raises-section",
            "Used when a function that raises exceptions is missing a Raises section.",
        ),
        "E9006": (
            "Function '%s' docstring missing summary line (Google Style)",
            "missing-summary-line",
            "Google Style: missing short description at the start of docstring.",
        ),
    }

    options = (
        (
            "docstring-min-lines",
            {
                "default": 0,
                "type": "int",
                "help": "Minimum number of lines in a function to require a docstring",
            },
        ),
    )

    # Statistics
    total_functions = 0
    with_docstring = 0
    without_docstring = 0
    vietnamese_docstring = 0
    english_or_japanese_docstring = 0
    google_style_compliant = 0

    # Regex patterns
    JAPANESE_REGEX = re.compile(r"[\u3040-\u30FF\u4E00-\u9FFF]")
    VIETNAMESE_REGEX = re.compile(
        r"[ăâđêôơưàáạảãèéẹẻẽìíịỉĩòóọỏõùúụủũỳýỵỷỹĂÂĐÊÔƠƯÀÁẠẢÃÈÉẸẺẼÌÍỊỈĨÒÓỌỎÕÙÚỤỦŨỲÝỴỶỸ]"
    )

    ARGS_SECTION = re.compile(
        r"Args?:\n(?:\s{2,}\w+(?: *\([^)]*\))?: .+(?:\n|$))+",
        re.MULTILINE,
    )
    RETURNS_SECTION = re.compile(
        r"Returns?:\n\s{2,}[\w\[\], |.<>():]+: .+(?:\n|$)",
        re.MULTILINE,
    )
    RAISES_SECTION = re.compile(
        r"Raises?:\n(?:\s{2,}[\w.]+: .+(?:\n|$))+",
        re.MULTILINE,
    )

    def visit_functiondef(self, node):
        self.total_functions += 1
        docstring = node.doc_node.value if node.doc_node else None

        # 1️⃣ Check for existence
        if not docstring:
            self.without_docstring += 1
        else:
            self.with_docstring += 1

            # 2️⃣ Language detection
            if self.VIETNAMESE_REGEX.search(docstring):
                self.vietnamese_docstring += 1
                self.add_message("vietnamese-docstring-detected", node=node, args=(node.name,))
            else:
                self.english_or_japanese_docstring += 1

            # 3️⃣ Google Style compliance
            if self._check_google_style(node, docstring):
                self.google_style_compliant += 1

        # 4️⃣ Long function without docstring
        num_lines = node.tolineno - node.fromlineno
        if num_lines > self.linter.config.docstring_min_lines and not node.doc_node:
            self.add_message(
                "missing-docstring-for-long-function",
                node=node,
                args=(node.name, self.linter.config.docstring_min_lines),
            )

    def _check_google_style(self, node, docstring: str) -> bool:
        """Check compliance with Google Python Style Guide."""
        compliant = True

        # 1️⃣ Check summary line
        lines = [l.strip() for l in docstring.strip().splitlines() if l.strip()]
        if not lines or not re.match(r"^[A-Z].*\.$", lines[0]):
            self.add_message("missing-summary-line", node=node, args=(node.name,))
            compliant = False

        # 2️⃣ Check Args section
        has_params = bool(node.args.args)
        if has_params and not self.ARGS_SECTION.search(docstring):
            self.add_message("missing-args-section", node=node, args=(node.name,))
            compliant = False

        # 3️⃣ Check Returns section
        has_return = any(
            getattr(stmt, "value", None) is not None
            and stmt.value.__class__.__name__ == "Return"
            for stmt in node.body
        )
        if has_return and not self.RETURNS_SECTION.search(docstring):
            self.add_message("missing-returns-section", node=node, args=(node.name,))
            compliant = False

        # 4️⃣ Check Raises section
        has_raise = any(stmt.__class__.__name__ == "Raise" for stmt in node.body)
        if has_raise and not self.RAISES_SECTION.search(docstring):
            self.add_message("missing-raises-section", node=node, args=(node.name,))
            compliant = False

        return compliant

    def close(self):
        """Print summary at the end."""
        if isinstance(self.linter.reporter, TextReporter):
            total = self.total_functions
            with_doc = self.with_docstring
            percent_doc = (with_doc / total * 100) if total > 0 else 0.0

            google_total = self.english_or_japanese_docstring
            google_percent = (
                (self.google_style_compliant / google_total * 100)
                if google_total > 0
                else 0.0
            )

            self.linter.reporter.writeln("\n📊 Docstring Summary Report:")
            self.linter.reporter.writeln(f"- Total functions:                 {total}")
            self.linter.reporter.writeln(f"- With docstring:                  {with_doc}")
            self.linter.reporter.writeln(f"- Without docstring:               {self.without_docstring}")
            self.linter.reporter.writeln(f"- Vietnamese docstrings:           {self.vietnamese_docstring}")
            self.linter.reporter.writeln(f"- English/Japanese docstrings:     {self.english_or_japanese_docstring}")
            self.linter.reporter.writeln(f"- Google-style compliant:          {self.google_style_compliant}")
            self.linter.reporter.writeln(f"- Google Style Coverage:           {google_percent:.2f}%")
            self.linter.reporter.writeln(f"- Coverage:                        {percent_doc:.2f}%\n")


def register(linter: PyLinter):
    linter.register_checker(DocstringLanguageChecker(linter))
