"""Shared recognition of TSL codegen API headings and declaration lines."""

from bisect import bisect_right
from dataclasses import dataclass
import re


TOP_LEVEL_RE = re.compile(r"^##(?!#)\s+`(.+?)`\s*$")
PLAIN_H2_RE = re.compile(r"^##(?!#)\s+")
BARE_API_RE = re.compile(r"^(#{3,5})(?!#)\s+`(.+?)`\s*$")
LEGACY_TYPED_RE = re.compile(
    r"^(#{3,5})(?!#)\s+"
    r"(class function|static function|static field|static const|"
    r"function|property|field|const|var|class)\s+`(.+?)`\s*$"
)
DECLARATION_LINE_RE = re.compile(r"^声明：(.*?)\s*$")
DECLARATION_PREFIX_RE = re.compile(r"^声明[:：]", re.IGNORECASE)
LOOSE_DECLARATION_RE = re.compile(r"^声明[:：]\s*(.*?)\s*$", re.IGNORECASE)
TOP_LEVEL_TYPES = {"function", "class", "unit"}
CLASS_DECLARATIONS = {
    "function": ("method", "instance"),
    "class function": ("method", "class"),
    "property": ("property", "instance"),
    "field": ("field", "instance"),
    "static field": ("field", "static"),
    "const": ("constant", "instance"),
    "static const": ("constant", "static"),
}
UNIT_DECLARATIONS = {
    "function": ("function", "unit"),
    "var": ("variable", "unit"),
    "const": ("constant", "unit"),
    "class": ("class", "unit"),
}
HEADING_RE = re.compile(r"^(#{1,6})(?!#)\s+")
FENCE_RE = re.compile(r"^\s*(```|~~~)")


@dataclass(frozen=True)
class ApiHeading:
    level: int
    kind: str
    binding: str
    name: str
    signature: str
    visible_title: str
    root_kind: str
    valid: bool = True
    error: str = ""


@dataclass(frozen=True)
class ApiEntry:
    heading: ApiHeading
    start: int
    end: int


def simple_name(signature):
    return signature.split("(", 1)[0].strip()


def fence_flags(lines):
    flags = []
    in_fence = False
    marker = ""
    for line in lines:
        stripped = line.lstrip()
        if not in_fence and FENCE_RE.match(line):
            marker = stripped[:3]
            flags.append(True)
            in_fence = True
            continue
        flags.append(in_fence)
        if in_fence and stripped.startswith(marker):
            in_fence = False
            marker = ""
    return flags


def heading_body_end(lines, start, flags=None):
    if flags is None:
        flags = fence_flags(lines)
    for index in range(start + 1, len(lines)):
        if flags[index]:
            continue
        if HEADING_RE.match(lines[index]):
            return index
    return len(lines)


def top_level_header_end(lines, start, flags=None):
    return heading_body_end(lines, start, flags)


def _declaration(lines, start, end, flags):
    content = []
    declaration_lines = []
    for index in range(start + 1, end):
        if flags[index]:
            continue
        stripped = lines[index].strip()
        if not stripped:
            continue
        content.append((index, stripped))
        if DECLARATION_PREFIX_RE.match(stripped):
            declaration_lines.append((index, stripped))

    if not content:
        return "", "API 标题后的第一条非空正文必须是 `声明：...`"
    first_index, first = content[0]
    match = DECLARATION_LINE_RE.fullmatch(first)
    if not match:
        loose = LOOSE_DECLARATION_RE.fullmatch(first)
        if loose and loose.group(1).strip():
            expected = f"声明：{loose.group(1).strip().casefold()}"
            return "", f"声明行必须精确写为 `{expected}`"
        return (
            "",
            "API 标题后的第一条非空正文必须是规范的 `声明：...` 行",
        )
    value = match.group(1).strip().casefold()
    expected = f"声明：{value}"
    if first != expected:
        return "", f"声明行必须精确写为 `{expected}`"
    if len(declaration_lines) != 1 or declaration_lines[0][0] != first_index:
        return "", "每个 API 必须且只能包含一行声明"
    return value, ""


def top_level_type(lines, start, end):
    return _declaration(lines, start, end, fence_flags(lines))


def invalid_heading(level, name, signature, visible_title, root_kind, message):
    return ApiHeading(
        level,
        "invalid",
        "",
        name,
        signature,
        visible_title,
        root_kind,
        False,
        message,
    )


def _valid_heading(level, kind, binding, signature, root_kind):
    return ApiHeading(
        level,
        kind,
        binding,
        simple_name(signature),
        signature,
        signature,
        root_kind,
    )


def top_level_heading(signature, declaration, error):
    if error:
        return invalid_heading(
            2,
            simple_name(signature),
            signature,
            signature,
            "",
            error,
        )
    if declaration not in TOP_LEVEL_TYPES:
        return invalid_heading(
            2,
            simple_name(signature),
            signature,
            signature,
            "",
            f"顶级 API 声明必须是 function、class 或 unit，实为 {declaration}",
        )
    return _valid_heading(2, declaration, "", signature, declaration)


def member_heading(
    level,
    declaration,
    signature,
    root_kind,
    unit_class_open,
    error,
):
    name = simple_name(signature)
    if error:
        return invalid_heading(
            level, name, signature, signature, root_kind, error
        )
    if declaration == "static function":
        return invalid_heading(
            level,
            name,
            signature,
            signature,
            root_kind,
            "不存在 static function，请使用 class function",
        )

    if root_kind == "class":
        if level != 3 or declaration not in CLASS_DECLARATIONS:
            return invalid_heading(
                level,
                name,
                signature,
                signature,
                root_kind,
                "class API 成员必须使用约定的 H3 声明",
            )
        kind, binding = CLASS_DECLARATIONS[declaration]
        return _valid_heading(level, kind, binding, signature, root_kind)

    if root_kind == "unit":
        if level == 3:
            if declaration not in UNIT_DECLARATIONS:
                return invalid_heading(
                    level,
                    name,
                    signature,
                    signature,
                    root_kind,
                    "unit interface 成员必须使用约定的 H3 声明",
                )
            kind, binding = UNIT_DECLARATIONS[declaration]
            return _valid_heading(level, kind, binding, signature, root_kind)
        if (
            level == 4
            and unit_class_open
            and declaration in CLASS_DECLARATIONS
        ):
            kind, binding = CLASS_DECLARATIONS[declaration]
            return _valid_heading(level, kind, binding, signature, root_kind)
        return invalid_heading(
            level,
            name,
            signature,
            signature,
            root_kind,
            "unit class 成员必须位于所属 class 的 H4",
        )

    return invalid_heading(
        level,
        name,
        signature,
        signature,
        root_kind,
        "function 顶级 API 不能包含 API 成员",
    )


def collect_headings_and_boundaries(lines):
    flags = fence_flags(lines)
    headings = []
    h2_boundaries = []
    root_kind = ""
    unit_class_open = False

    for index, line in enumerate(lines):
        if flags[index]:
            continue

        if PLAIN_H2_RE.match(line):
            h2_boundaries.append(index)
            root_kind = ""
            unit_class_open = False
            top_level = TOP_LEVEL_RE.match(line)
            if not top_level:
                continue
            signature = top_level.group(1)
            end = heading_body_end(lines, index, flags)
            declaration, error = _declaration(
                lines, index, end, flags
            )
            heading = top_level_heading(signature, declaration, error)
            if heading.valid:
                root_kind = heading.kind
            headings.append((index, heading))
            continue

        if not root_kind:
            continue

        heading_match = HEADING_RE.match(line)
        if root_kind == "unit" and heading_match:
            if len(heading_match.group(1)) == 3:
                unit_class_open = False

        legacy = LEGACY_TYPED_RE.match(line)
        if legacy:
            level = len(legacy.group(1))
            signature = legacy.group(3)
            headings.append(
                (
                    index,
                    invalid_heading(
                        level,
                        simple_name(signature),
                        signature,
                        line.lstrip("# "),
                        root_kind,
                        "API 标题只写名称或调用签名，声明种类写在声明行",
                    ),
                )
            )
            continue

        bare = BARE_API_RE.match(line)
        if not bare:
            continue
        level = len(bare.group(1))
        signature = bare.group(2)
        end = heading_body_end(lines, index, flags)
        declaration, error = _declaration(lines, index, end, flags)
        heading = member_heading(
            level,
            declaration,
            signature,
            root_kind,
            unit_class_open,
            error,
        )
        if root_kind == "unit" and level == 3:
            unit_class_open = heading.valid and heading.kind == "class"
        headings.append((index, heading))

    return headings, h2_boundaries


def collect_headings(lines):
    headings, _ = collect_headings_and_boundaries(lines)
    return headings


def iter_api_entries(lines):
    headings, h2_boundaries = collect_headings_and_boundaries(lines)
    for index, (start, heading) in enumerate(headings):
        next_heading = (
            headings[index + 1][0]
            if index + 1 < len(headings)
            else len(lines)
        )
        boundary_index = bisect_right(h2_boundaries, start)
        next_h2 = (
            h2_boundaries[boundary_index]
            if boundary_index < len(h2_boundaries)
            else len(lines)
        )
        yield ApiEntry(heading, start, min(next_heading, next_h2))


def slug(text):
    return re.sub(r"[^a-z0-9_]", "", text.casefold())
