"""Shared helpers: locate the project, parse change fragments (Markdown + YAML-like front matter).

Only the YAML subset used by the template is supported on purpose, so the tools
need nothing beyond the Python standard library.
"""
import pathlib
import re

ROOT = pathlib.Path(__file__).resolve().parents[2]
RULES = ROOT / "RULEFOLDER"
CHANGES = RULES / "changes"
TEAM = RULES / "TEAM.md"
REGISTRY = RULES / "FEATURE-REGISTRY.md"
CHANGELOG = RULES / "CHANGELOG.md"
API_DOC = ROOT / "DOC" / "API.md"
ROUTES_DIR = ROOT / "app" / "routes"

TYPES = ["feature", "bugfix", "refactor", "security", "docs", "infra", "test", "chore"]
SCOPES = ["backend", "api", "web-ui", "infra", "docs", "project-wide"]
REQUIRED = ["id", "title", "date", "author", "type", "scope", "status", "security_impact"]
ID_RE = re.compile(r"^CHG-(\d{3}|\d{8}-[A-Z]{2,3}-\d{2})$")

_FM_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n?(.*)$", re.S)


def _scalar(v):
    v = v.strip()
    if len(v) >= 2 and v[0] == v[-1] and v[0] in "\"'":
        return v[1:-1]
    low = v.lower()
    if low in ("true", "yes"):
        return True
    if low in ("false", "no"):
        return False
    return v


def _split_list(s):
    return [_scalar(x) for x in re.findall(r'"[^"]*"|\'[^\']*\'|[^,]+', s) if x.strip()]


def parse_frontmatter(text):
    m = _FM_RE.match(text)
    if not m:
        return None, text
    data, key = {}, None
    for line in m.group(1).splitlines():
        if not line.strip() or line.lstrip().startswith("#"):
            continue
        stripped = line.strip()
        if stripped.startswith("- ") and key is not None:
            cur = data.get(key)
            if not isinstance(cur, list):
                data[key] = [] if cur in ("", None) else [cur]
            data[key].append(_scalar(stripped[2:]))
            continue
        if ":" not in line:
            continue
        key, _, val = line.partition(":")
        key, val = key.strip(), val.strip()
        if val.startswith("[") and val.endswith("]"):
            inner = val[1:-1].strip()
            data[key] = _split_list(inner) if inner else []
        else:
            data[key] = _scalar(val)
    return data, m.group(2)


def load_fragments():
    """Return fragments sorted by (date, id). Each is a dict with 'meta', 'body', 'path'."""
    out = []
    for p in sorted(CHANGES.glob("*.md")):
        if p.name.startswith("_"):
            continue
        meta, body = parse_frontmatter(p.read_text(encoding="utf-8"))
        out.append({"meta": meta or {}, "body": body, "path": p})
    out.sort(key=lambda f: (str(f["meta"].get("date", "")), str(f["meta"].get("id", ""))))
    return out


def as_list(v):
    if v is None or v == "":
        return []
    return v if isinstance(v, list) else [v]


def read_team():
    """Return ({email: code}, {code: name}) parsed from the table in TEAM.md.

    The email cell may hold several addresses separated by spaces or commas.
    """
    by_email, names = {}, {}
    if not TEAM.exists():
        return by_email, names
    for line in TEAM.read_text(encoding="utf-8").splitlines():
        cells = [c.strip() for c in line.strip().strip("|").split("|")]
        if len(cells) >= 3 and re.fullmatch(r"[A-Z]{2,3}", cells[0]):
            names[cells[0]] = cells[1]
            for token in re.split(r"[\s,]+", cells[2]):
                if "@" in token:
                    by_email[token.lower()] = cells[0]
    return by_email, names
