from __future__ import annotations

import re


class ApiEndpointCollector:
    _METHODS = ("GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS")
    _ENDPOINT_VALUE_RE = re.compile(
        r"\b((?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)(?:\s*\|\s*(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS))*)\s+(/[-a-zA-Z0-9_./{}]+)"
    )
    _DOC_EXTS = (".md", ".yaml", ".yml", ".json")

    def collect(self, rows: list[dict]) -> list[str]:
        endpoints: list[str] = []
        for row in rows:
            self._append_from_endpoint_metadata(endpoints, row)
        return sorted(set(endpoints))

    def _append_from_endpoint_metadata(self, out: list[str], row: dict) -> None:
        metadata = dict(row.get("metadata") or {})
        endpoint_value = str(metadata.get("endpoint") or "").strip()
        if not endpoint_value:
            return
        for methods, path in self._ENDPOINT_VALUE_RE.findall(endpoint_value):
            self._append_methods_with_path(out, methods, path)

    def _append_methods_with_path(self, out: list[str], methods_raw: str, path_raw: str) -> None:
        methods = [
            part.strip().upper()
            for part in str(methods_raw or "").split("|")
            if part.strip().upper() in self._METHODS
        ]
        if not methods:
            self._append_default(out, path_raw)
            return
        for method in methods:
            self._append_with_method(out, method, path_raw)

    def _append_default(self, out: list[str], raw: str) -> None:
        self._append_with_method(out, "GET", raw)

    def _append_with_method(self, out: list[str], method: str, raw: str) -> None:
        value = str(raw or "").strip().strip("`'\"()[].,:;!?").lower()
        if not value.startswith("/"):
            return
        if value.endswith(self._DOC_EXTS):
            return
        if len(value.split("/")) < 2:
            return
        endpoint = f"{method.upper()} {value}"
        if endpoint not in out:
            out.append(endpoint)
