import re
from typing import Any, Optional

from pydantic import BaseModel, field_validator


class KConfigDocItem(BaseModel):
    """Represents a single Kconfig item (from kconfig.json generated by Zephyr Documentation System) with all its metadata and relationships."""

    name: str
    prompt: Optional[str] = None
    type: str
    help: Optional[str] = None

    dependencies: str = ""

    defaults: list[str] = []
    selects: list[str] = []
    selected_by: list[str] = []
    implies: list[str] = []
    implied_by: list[str] = []
    ranges: list[str] = []
    choices: list[str] = []

    alt_defaults: list[str] = []
    filename: str
    linenr: int
    menupath: str

    @staticmethod
    def _strip_tags(text: str) -> str:
        if not isinstance(text, str):
            return str(text) if text is not None else ""
        return re.sub(r"<[^>]+>", "", text)

    @staticmethod
    def _clean_logic_string(text: str) -> str:
        if not text:
            return ""
        text = re.sub(r"<[^>]+>", "", text)
        text = text.replace("&lt", "<").replace("&gt", ">")
        text = re.sub(r"<choice\s+([^>]+)>", r"\1", text)
        return text.strip()

    @staticmethod
    def _shorten_paths(text: str) -> str:
        """
        Detects standard Zephyr workspace directories and removes
        the user-specific absolute path prefix before them.
        """
        # The known roots of a Zephyr workspace
        workspace_roots = r"(zephyr|bootloader|modules|tools)"

        # Regex explanation:
        # /[^\s"]*?           -> Match an absolute path start (/) and non-quote chars
        # /                   -> Match the slash before the root folder
        # (zephyr|...)        -> Capture the root folder name
        # /                   -> Match the trailing slash
        #
        # We replace the matched prefix with just "rootname/"
        pattern = rf'/[^\s"]*?/({workspace_roots})/'

        # Input:  "/Users/.../deps/bootloader/mcuboot/file.pem"
        # Match:  "/Users/.../deps/bootloader/"
        # Replace: "bootloader/"
        return re.sub(pattern, r"\1/", text)

    # ---------------------------------------------------------
    # Validators
    # ---------------------------------------------------------

    @field_validator("dependencies", mode="before")
    @classmethod
    def validate_dependencies(cls, v: Any) -> str:
        if v is None:
            return ""
        return cls._clean_logic_string(str(v))

    @field_validator(
        "defaults",
        "alt_defaults",
        "selects",
        "selected_by",
        "implies",
        "implied_by",
        "ranges",
        "choices",
        mode="before",
    )
    @classmethod
    def validate_list_fields(cls, v: Any) -> list[str]:
        if v is None:
            return []

        items = v if isinstance(v, list) else [v]

        cleaned_items = []
        for item in items:
            # 1. Clean HTML tags
            text = cls._strip_tags(item)
            # 2. Shorten paths based on Zephyr workspace structure
            text = cls._shorten_paths(text)

            cleaned_items.append(text.strip())

        return cleaned_items


class KConfigDoc(BaseModel):
    """Represents the entire Kconfig documentation graph, containing multiple KConfigDocItems."""

    gh_base_url: str
    zephyr_version: str
    symbols: list[KConfigDocItem] = []

    def get_symbols(self, name: str, exact: bool = False) -> list[KConfigDocItem]:
        """Return the KConfigDocItems that start with the given name, or None if not found."""
        results = []
        for s in self.symbols:
            if exact:
                if s.name == name:
                    results.append(s)
            else:
                if s.name.startswith(name):
                    results.append(s)
        return results
