#!/usr/bin/env python3
"""Query current edubuntu-menu-admin state and output JSON."""

import grp
import json
import os
import pwd
import sys
from pathlib import Path

SYSTEM_APPS_DIR = Path("/usr/share/applications")
SNAP_APPS_DIR = Path("/var/lib/snapd/desktop/applications")
OVERRIDE_DIR = Path("/usr/share/edubuntu/applications")
SKIP_FILES = frozenset({"defaults.list", "mimeinfo.cache", "kde-mimeapps.list"})


def has_no_display(filepath):
    try:
        with open(filepath, encoding="utf-8", errors="replace") as fh:
            for line in fh:
                if line.strip().lower() == "nodisplay=true":
                    return True
    except OSError:
        pass
    return False


def get_name(filepath):
    try:
        with open(filepath, encoding="utf-8", errors="replace") as fh:
            for line in fh:
                if line.startswith("Name="):
                    return line.split("=", 1)[1].strip()
    except OSError:
        pass
    return ""


def scan_directory(directory):
    entries = []
    if not directory.is_dir():
        return entries
    for name in sorted(os.listdir(directory)):
        if name in SKIP_FILES or not name.endswith(".desktop"):
            continue
        filepath = directory / name
        if not filepath.is_file():
            continue
        if has_no_display(filepath):
            continue
        entries.append({
            "id": name,
            "name": get_name(filepath),
            "hidden_global": (OVERRIDE_DIR / name).exists(),
        })
    return entries


def build_app_table():
    seen = set()
    entries = []
    for d in (SYSTEM_APPS_DIR, SNAP_APPS_DIR):
        for e in scan_directory(d):
            if e["id"] not in seen:
                seen.add(e["id"])
                entries.append(e)
    return entries


def adm_members():
    try:
        return frozenset(grp.getgrnam("adm").gr_mem)
    except KeyError:
        return frozenset()


_NOLOGIN_SHELLS = frozenset({
    "/usr/sbin/nologin",
    "/sbin/nologin",
    "/bin/false",
    "/bin/sync",
    "/usr/bin/false",
})


def list_non_admin_users():
    adm = adm_members()
    users = []
    for pw in pwd.getpwall():
        if (
            pw.pw_uid >= 1000
            and pw.pw_name not in ("nobody", "nfsnobody")
            and pw.pw_name not in adm
            and pw.pw_shell not in _NOLOGIN_SHELLS
        ):
            users.append(pw.pw_name)
    return sorted(users)


def user_apps_dir(username):
    pw = pwd.getpwnam(username)
    return Path(pw.pw_dir) / ".local" / "share" / "applications"


def user_hidden_set(username, app_ids):
    udir = user_apps_dir(username)
    hidden = []
    for aid in app_ids:
        p = udir / aid
        if p.exists() and has_no_display(p):
            hidden.append(aid)
    return hidden


def main():
    apps = build_app_table()
    app_ids = [a["id"] for a in apps]
    users = list_non_admin_users()
    user_data = []
    for u in users:
        user_data.append({
            "username": u,
            "hidden": user_hidden_set(u, app_ids),
        })

    json.dump({"apps": apps, "users": user_data}, sys.stdout)


if __name__ == "__main__":
    main()
