# SPDX-License-Identifier: Apache-2.0

from gitlint.rules import LineRule, RuleViolation, CommitMessageBody
from typing import List, Optional
import re


IGNORE_PREFIXES = [
    # Please sort alphabetically
    " ",
    "Acked-by: ",
    "Closes: ",
    "Co-authored-by: ",
    "Co-developed-by: ",
    "Debugged-by: ",
    "Diagnosed-by: ",
    "Explained-by: ",
    "Fixed-by: ",
    "Fixes: ",
    "Helped-by: ",
    "Inspired-by: ",
    "Link: ",
    "On-behalf-of: ",
    "Originally-by: ",
    "Reported-by: ",
    "Reviewed-and-tested-by: ",
    "Reviewed-by: ",
    "Signed-off-by: ",
    "Suggested-by: ",
    "Tested-by: ",
    "Triggered-by: ",
    "\t",
]

# Pattern allowing:
# - [0]: https://example.com
# - [0] https://example.com
# - https://example.com
LINK_REGEX = re.compile(r"^(([\[0-9]+]:?\s?)?https?://).*$")

MAX_LEN = 72


class BodyMaxLineLengthEx(LineRule):
    """
    A rule to enforce a line limit of 72 characters, except for valid cases:

    - Markdown-style code blocks
    - Commit tags, such as Signed-off-by
    - Links
    """

    # A rule MUST have a human friendly name
    name = "body-max-line-length-ex"

    # A rule MUST have a *unique* id.
    # We recommend starting with UL (for User-defined Line-rule)
    id = "UL-ll"

    # A line-rule MUST have a target (not required for CommitRules).
    target = CommitMessageBody

    # Updated property as the commit messages is validated line by line.
    inside_open_codeblock = False

    def validate(self, line, commit) -> Optional[List[RuleViolation]]:
        # We keep track of whether we are in an open code block.
        is_codeblock_marker = line.startswith("```")
        inside_open_codeblock_ = self.inside_open_codeblock
        if is_codeblock_marker:
            self.inside_open_codeblock = not self.inside_open_codeblock

        # Begin checks
        if len(line) <= MAX_LEN:
            return None

        if inside_open_codeblock_:
            return None

        if None is not LINK_REGEX.match(line):
            return None

        # Don't check lines with allowed prefixes
        for prefix in IGNORE_PREFIXES:
            if line.startswith(prefix):
                return None

        return [
            RuleViolation(
                self.id,
                f"Line '{line}' exceeds limit of {MAX_LEN}: {len(line)}",
            )
        ]
