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


class ClassDocstringChecker(BaseChecker):
    """
    Checker for verifying class-level docstrings follow Google Python Style Guide.
    """

    name = "class-docstring-checker"
    priority = -1
    msgs = {
        "E9101": (
            "Class '%s' is missing a docstring",
            "missing-google-class-docstring",
            "Used when a public class has no docstring.",
        ),
        "E9102": (
            "Class '%s' docstring missing summary line (Google Style)",
            "missing-google-class-summary-line",
            "Google Style: missing short description at the start of class docstring.",
        ),
        "E9103": (
            "Class '%s' docstring missing Attributes section (Google Style)",
            "missing-google-attributes-section",
            "Used when a class defines attributes but lacks an Attributes section in docstring.",
        ),
    }

    # Regex pattern for detecting Attributes section in Google Style
    ATTRIBUTES_SECTION = re.compile(
        r"Attributes:\n(?:\s{2,}\w+(?: *\([^)]*\))?: .+(?:\n|$))+",
        re.MULTILINE,
    )

    total_classes = 0
    with_docstring = 0
    without_docstring = 0
    compliant = 0

    def visit_classdef(self, node):
        """Visit each class definition."""
        self.total_classes += 1
        docstring = node.doc_node.value if node.doc_node else None

        if not docstring:
            # Missing docstring
            self.without_docstring += 1
            self.add_message("missing-google-class-docstring", node=node, args=(node.name,))
            return

        self.with_docstring += 1
        is_compliant = True

        # 1️⃣ Check summary line (Google Style: starts with capital, ends with '.')
        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-google-class-summary-line", node=node, args=(node.name,))
            is_compliant = False

        # 2️⃣ Detect attributes (self.xxx = ...) both in class and in methods
        attributes = set()

        for stmt in node.body:
            # Check top-level assignment: xxx = ...
            if hasattr(stmt, "targets"):
                for target in stmt.targets:
                    if getattr(target, "attrname", None):
                        attributes.add(target.attrname)

            # Also check inside methods (e.g., def __init__(...): self.xxx = ...)
            if hasattr(stmt, "body"):
                for inner in getattr(stmt, "body", []):
                    try:
                        if hasattr(inner, "targets"):
                            for target in inner.targets:
                                if getattr(target, "attrname", None):
                                    attributes.add(target.attrname)
                    except Exception:
                        continue

        # 3️⃣ Check Attributes section content
        if attributes:
            if not self.ATTRIBUTES_SECTION.search(docstring):
                attr_list = ", ".join(sorted(attributes))
                self.add_message(
                    "missing-google-attributes-section",
                    node=node,
                    args=(f"{node.name} ({attr_list})",),
                )
                is_compliant = False

        if is_compliant:
            self.compliant += 1

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

            self.linter.reporter.writeln("\n📘 Class Docstring Summary Report:")
            self.linter.reporter.writeln(f"- Total classes:                   {total}")
            self.linter.reporter.writeln(f"- With docstring:                  {self.with_docstring}")
            self.linter.reporter.writeln(f"- Without docstring:               {self.without_docstring}")
            self.linter.reporter.writeln(f"- Google-style compliant:          {self.compliant}")
            self.linter.reporter.writeln(f"- Docstring coverage:              {percent_doc:.2f}%")
            self.linter.reporter.writeln(f"- Google Style coverage:           {google_percent:.2f}%\n")


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