#!/usr/bin/env python3
"""Convert a cheatsheet .md file (cheatsheet.md, or a linked sub-sheet
alongside it) into the JSON format cheatsheet_overlay renders: a list of
{"name": <category>, "shortcuts": [{"key", "description"[, "link"]}, ...]}.
(Originally written to feed the "Shortcuts" gnome-shell extension's
shortcuts-file setting -- that extension is no longer installed, see
startup_bernina_terminal's "Defensive cleanup" comment -- cheatsheet_overlay
is now this JSON's only consumer, so its shape is free to extend.)

Usage: cheatsheet_to_shortcuts_json <input.md> <output.json>

Only emits a category for a heading that is immediately followed (after
blank lines) by a markdown table -- headings followed by prose/bullet lists
(Table of contents, Screen & session behavior) are intentionally skipped,
since they aren't keyboard shortcuts. The category name is always the
*nearest* preceding heading, so a "##" section containing "###" subsections
each with their own table (Compose key) yields one category per "###", not
one lumped-together "##" category.

A first-column cell written as a markdown link, e.g.
`[Admin & Account Setup](cheatsheet_admin.md)` in a Command/Shortcut/Sheet
table, becomes a navigable entry instead of a plain shortcut: cheatsheet_overlay
shows it as a clickable link and, on click, loads and displays *that* file
in place of the current one (path resolved relative to the linking file's
own directory), rather than treating ".md" as a literal key combo.
"""
import json
import re
import sys

CELL_STRIP = re.compile(r"`|\*\*")
MD_LINK = re.compile(r"^\[([^\]]+)\]\(([^)]+\.md)\)$")


def clean(text):
    text = CELL_STRIP.sub("", text).strip()
    text = text.replace("\\|", "|")
    return re.sub(r"\s+", " ", text)


def parse_row(line):
    # drop leading/trailing '|' then split on unescaped '|'
    inner = line.strip()
    if inner.startswith("|"):
        inner = inner[1:]
    if inner.endswith("|"):
        inner = inner[:-1]
    cells = re.split(r"(?<!\\)\|", inner)
    return [clean(c) for c in cells]


def is_separator(line):
    return bool(re.match(r"^\s*\|?[\s:|-]+\|?\s*$", line)) and "-" in line


def parse(md_text):
    lines = md_text.splitlines()
    categories = []
    heading = None
    i = 0
    n = len(lines)
    while i < n:
        line = lines[i]
        heading_match = re.match(r"^#{1,6}\s+(.*)", line)
        if heading_match:
            heading = clean(heading_match.group(1))
            i += 1
            continue

        if line.strip().startswith("|") and i + 1 < n and is_separator(lines[i + 1]):
            header_cells = parse_row(line)
            i += 2  # skip header + separator
            rows = []
            while i < n and lines[i].strip().startswith("|"):
                rows.append(parse_row(lines[i]))
                i += 1

            shortcuts = build_shortcuts(header_cells, rows)
            if shortcuts and heading:
                categories.append({"name": heading, "shortcuts": shortcuts})
            continue

        i += 1
    return categories


def build_shortcuts(header, rows):
    ncols = len(header)

    # the paired Greek-letters table: Key/Lower/Name/Upper repeated twice
    if ncols == 8:
        out = []
        for row in rows:
            for base in (0, 4):
                key, lower, name, upper = row[base : base + 4]
                if not key:
                    continue
                desc = f"{lower} {name}" + (f" / {upper} {name.capitalize()}" if upper and upper != "—" else "")
                out.append({"key": f"CapsLock Space {key}", "description": desc})
        return out

    # Sequence | Result | Meaning  (built-in accent sequences)
    if ncols == 3 and header[0].lower().startswith("sequence"):
        return [
            {"key": row[0], "description": f"{row[1]} — {row[2]}"}
            for row in rows
            if row[0]
        ]

    # Shortcut | Action [| Runs]  (app shortcuts, gTile, primary-controls)
    # Command | Description        (the bin/ script index)
    # Sheet | Description          (links to other cheat sheets)
    if ncols >= 2 and header[0].lower().startswith(("shortcut", "command", "sheet")):
        out = []
        for row in rows:
            if not row[0]:
                continue
            m = MD_LINK.match(row[0])
            if m:
                out.append({"key": m.group(1), "description": row[1], "link": m.group(2)})
            else:
                out.append({"key": row[0], "description": row[1]})
        return out

    return []


def main():
    if len(sys.argv) != 3:
        print(f"Usage: {sys.argv[0]} <input.md> <output.json>", file=sys.stderr)
        sys.exit(1)
    src, dest = sys.argv[1], sys.argv[2]
    with open(src, encoding="utf-8") as f:
        categories = parse(f.read())
    with open(dest, "w", encoding="utf-8") as f:
        json.dump(categories, f, ensure_ascii=False, indent=2)
        f.write("\n")


if __name__ == "__main__":
    main()
