"""
非 Python 源码的**轻量符号轮廓**（行级启发式，非完整 AST）。

适用于 AI **导航与定位**；可能在字符串/注释中误报。精确语义请依赖语言原生工具链或可选 Tree-sitter。
"""

from __future__ import annotations

import re
from pathlib import Path
from typing import Any


def _strip_line_comment(line: str, *, lang: str) -> str:
    s = line.strip()
    if lang in ("javascript", "typescript", "tsx", "jsx"):
        if s.startswith("//"):
            return ""
        # 粗略去掉行尾 // 注释（不处理字符串内 //）
        if "//" in line:
            i = line.find("//")
            if i >= 0:
                line = line[:i]
    if lang == "go":
        if "//" in line:
            i = line.find("//")
            if i >= 0:
                line = line[:i]
    return line.rstrip()


_RE_GO_PKG = re.compile(r"^\s*package\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*$")
_RE_GO_FUNC = re.compile(
    r"^\s*func\s+(?:\([^)]+\)\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*\("
)
_RE_GO_TYPE = re.compile(
    r"^\s*type\s+([A-Za-z_][A-Za-z0-9_]*)\s+(?:struct|interface)\b"
)

# 顶层/导出：不处理类体内方法（需 AST）；足够做「先找文件再找行」
_RE_JS_CLASS = re.compile(
    r"^\s*(?:export\s+)?(?:declare\s+)?(?:abstract\s+)?class\s+([A-Za-z_][A-Za-z0-9_]*)\b"
)
_RE_JS_FUNC = re.compile(
    r"^\s*(?:export\s+)?(?:declare\s+)?(?:async\s+)?function\s+([A-Za-z_][A-Za-z0-9_]*)\s*\("
)
_RE_JS_EXPORT_FN = re.compile(
    r"^\s*export\s+(?:default\s+)?(?:async\s+)?function\s+([A-Za-z_][A-Za-z0-9_]*)\s*\("
)


def _module_nodes_skeleton(
    path: Path,
    *,
    module_qname: str,
    line_count: int,
    source_lang: str,
    parser: str,
) -> tuple[list[dict[str, Any]], str, str]:
    fk = path.stem[:32] + "_" + hex(abs(hash(str(path.resolve()))) & 0xFFFF_FFFF)[2:12]
    root_id = f"{fk}-0"
    nodes: list[dict[str, Any]] = [
        {
            "id": root_id,
            "parent_id": None,
            "kind": "module",
            "name": module_qname.split(".")[-1] if "." in module_qname else module_qname,
            "qname": module_qname,
            "file": str(path.resolve()),
            "line_start": 1,
            "line_end": max(line_count, 1),
            "docstring_preview": None,
            "ai_description_slot": None,
            "source_lang": source_lang,
            "parser": parser,
        }
    ]
    return nodes, root_id, fk


def go_file_outline(
    path: Path, *, module_qname: str | None = None
) -> dict[str, Any]:
    p = path.resolve()
    try:
        text = p.read_text(encoding="utf-8")
    except OSError as e:
        return {"ok": False, "error": str(e)}
    lines = text.splitlines()
    pkg = p.stem
    root_q = module_qname if module_qname else pkg
    nodes, root_id, fk = _module_nodes_skeleton(
        p,
        module_qname=root_q,
        line_count=len(lines),
        source_lang="go",
        parser="heuristic",
    )
    next_i = 1
    for i, raw in enumerate(lines, start=1):
        line = _strip_line_comment(raw, lang="go")
        if not line.strip():
            continue
        m = _RE_GO_PKG.match(line)
        if m:
            pkg = m.group(1)
            if not module_qname:
                root_q = pkg
                nodes[0]["name"] = pkg
                nodes[0]["qname"] = pkg
            continue
        m = _RE_GO_FUNC.match(line)
        if m:
            name = m.group(1)
            qn = f"{root_q}.{name}"
            nid = f"{fk}-{next_i}"
            next_i += 1
            nodes.append(
                {
                    "id": nid,
                    "parent_id": root_id,
                    "kind": "function",
                    "name": name,
                    "qname": qn,
                    "file": str(p),
                    "line_start": i,
                    "line_end": i,
                    "docstring_preview": None,
                    "ai_description_slot": None,
                    "source_lang": "go",
                    "parser": "heuristic",
                }
            )
            continue
        m = _RE_GO_TYPE.match(line)
        if m:
            name = m.group(1)
            qn = f"{root_q}.{name}"
            nid = f"{fk}-{next_i}"
            next_i += 1
            nodes.append(
                {
                    "id": nid,
                    "parent_id": root_id,
                    "kind": "class",
                    "name": name,
                    "qname": qn,
                    "file": str(p),
                    "line_start": i,
                    "line_end": i,
                    "docstring_preview": None,
                    "ai_description_slot": None,
                    "source_lang": "go",
                    "parser": "heuristic",
                }
            )
    nodes[0]["line_end"] = max(len(lines), 1)
    return {"ok": True, "path": str(p), "module_qname": root_q, "nodes": nodes}


def _js_ts_lang(path: Path) -> str:
    s = path.suffix.lower()
    if s in (".tsx",):
        return "tsx"
    if s in (".jsx",):
        return "jsx"
    if s in (".ts",):
        return "typescript"
    return "javascript"


def javascript_file_outline(
    path: Path, *, module_qname: str | None = None
) -> dict[str, Any]:
    p = path.resolve()
    lang = _js_ts_lang(p)
    try:
        text = p.read_text(encoding="utf-8")
    except OSError as e:
        return {"ok": False, "error": str(e)}
    lines = text.splitlines()
    root_q = module_qname if module_qname else p.stem
    nodes, root_id, fk = _module_nodes_skeleton(
        p,
        module_qname=root_q,
        line_count=len(lines),
        source_lang=lang,
        parser="heuristic",
    )
    next_i = 1
    for i, raw in enumerate(lines, start=1):
        line = _strip_line_comment(raw, lang=lang)
        if not line.strip():
            continue
        name: str | None = None
        kind = "function"
        for rx in (_RE_JS_EXPORT_FN, _RE_JS_FUNC, _RE_JS_CLASS):
            m = rx.match(line)
            if m:
                name = m.group(1)
                if "class" in rx.pattern:
                    kind = "class"
                break
        if not name:
            continue
        qn = f"{root_q}.{name}"
        nid = f"{fk}-{next_i}"
        next_i += 1
        nodes.append(
            {
                "id": nid,
                "parent_id": root_id,
                "kind": kind,
                "name": name,
                "qname": qn,
                "file": str(p),
                "line_start": i,
                "line_end": i,
                "docstring_preview": None,
                "ai_description_slot": None,
                "source_lang": lang,
                "parser": "heuristic",
            }
        )
    nodes[0]["line_end"] = max(len(lines), 1)
    return {"ok": True, "path": str(p), "module_qname": root_q, "nodes": nodes}
