#!/usr/bin/python3

# omarchy:summary=Pick files with the desktop file chooser
# omarchy:args=[--title <title>] [--multiple] [--directory] [--extensions "<ext ext...>"]
# omarchy:examples=omarchy file select --title "Send with Tailscale" --multiple | omarchy file select --title "Pick image" --extensions "png svg" | omarchy file select --title "Share folder" --directory

# Python rather than bash, alone among the commands here, because the portal
# answers a request with a Response signal addressed to the connection that
# asked, and D-Bus delivers a directed signal only to that connection. Every
# shell-callable client — gdbus call, busctl call, dbus-send — opens its own
# connection and exits before the answer arrives, and gdbus monitor registers
# with AddMatch rather than BecomeMonitor, so it never sees one either. Holding
# a single connection across both the call and the wait is the whole job, and
# bash has no way to hold one.

import argparse
import os
import sys

import gi

gi.require_version("Gio", "2.0")
from gi.repository import Gio, GLib

# A dialog nobody ever answers would otherwise keep this process, and whatever
# waits on its output, alive forever.
ANSWER_TIMEOUT_SEC = 600

# Callers act on these: nothing picked is a decision, a chooser that never ran
# is a fault, and the two want different handling.
EXIT_NOTHING_PICKED = 1
EXIT_CHOOSER_FAILED = 2


def main():
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("--title", default="Select file")
    parser.add_argument("--multiple", action="store_true")
    parser.add_argument("--directory", action="store_true")
    parser.add_argument("--extensions", default="")
    args, unknown = parser.parse_known_args()

    if unknown:
        print("omarchy-file-select: unknown option %s" % unknown[0], file=sys.stderr)
        return EXIT_CHOOSER_FAILED

    bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
    loop = GLib.MainLoop()
    uris = []

    def on_response(connection, sender, path, interface, signal, params):
        code, results = params.unpack()
        if code == 0:
            uris.extend(results.get("uris", []))
        loop.quit()

    def subscribe(path):
        bus.signal_subscribe(
            "org.freedesktop.portal.Desktop",
            "org.freedesktop.portal.Request",
            "Response",
            path,
            None,
            Gio.DBusSignalFlags.NONE,
            on_response,
        )

    # The request path is derived from our bus name and the token we pass, so it
    # can be subscribed to up front. Asking first would race a dialog that gets
    # answered immediately.
    token = "omarchy%d" % os.getpid()
    sender = bus.get_unique_name()[1:].replace(".", "_")
    predicted = "/org/freedesktop/portal/desktop/request/%s/%s" % (sender, token)
    subscribe(predicted)

    options = {
        "handle_token": GLib.Variant("s", token),
        "multiple": GLib.Variant("b", args.multiple),
    }

    if args.directory:
        options["directory"] = GLib.Variant("b", True)

    # Filters name file formats, which a directory chooser has no use for.
    if args.extensions and not args.directory:
        # Glob matching in the chooser is case-sensitive, so cover both cases.
        exts = [ext.lstrip(".").lower() for ext in args.extensions.split()]
        patterns = [(0, "*." + ext) for ext in exts] + [(0, "*." + ext.upper()) for ext in exts]
        label = " ".join("*." + ext for ext in exts)
        filters = GLib.Variant("a(sa(us))", [(label, patterns)])
        options["filters"] = filters
        options["current_filter"] = GLib.Variant("(sa(us))", (label, patterns))

    handle = bus.call_sync(
        "org.freedesktop.portal.Desktop",
        "/org/freedesktop/portal/desktop",
        "org.freedesktop.portal.FileChooser",
        "OpenFile",
        GLib.Variant("(ssa{sv})", ("", args.title, options)),
        None,
        Gio.DBusCallFlags.NONE,
        -1,
        None,
    ).unpack()[0]

    # Portals predating the token convention answer on a path of their choosing.
    if handle != predicted:
        subscribe(handle)

    GLib.timeout_add_seconds(ANSWER_TIMEOUT_SEC, loop.quit)
    loop.run()

    for uri in uris:
        print(GLib.filename_from_uri(uri)[0])

    return 0 if uris else EXIT_NOTHING_PICKED


if __name__ == "__main__":
    try:
        sys.exit(main())
    except GLib.Error as error:
        print("omarchy-file-select: %s" % error.message, file=sys.stderr)
        sys.exit(EXIT_CHOOSER_FAILED)
