#!/bin/bash

# omarchy:summary=Send an IPC call to the running Omarchy shell
# omarchy:args=<target> <method> [args...]
# omarchy:examples=omarchy shell shell ping | omarchy-shell menu toggle root

if [[ $# -eq 0 || $1 == "-h" || $1 == "--help" ]]; then
  cat <<USAGE
Usage: omarchy-shell <target> <method> [args...]

Forwards an IPC call to the supervised omarchy-shell service. The shell is
expected to already be running; this command does not start it.

Examples:
  omarchy-shell shell ping
  omarchy-shell shell summon omarchy.settings "{}"
  omarchy-shell shell listPlugins
  omarchy-shell menu toggle root
USAGE
  exit 0
fi

if (( $# < 2 )); then
  echo "Usage: omarchy-shell <target> <method> [args...]" >&2
  exit 1
fi

if [[ -z ${OMARCHY_PATH:-} ]]; then
  echo "OMARCHY_PATH is not set" >&2
  exit 1
fi

SHELL_QML="$OMARCHY_PATH/shell/shell.qml"

perl -MCwd=abs_path -MEncode=encode,decode -MSocket -MIO::Handle \
  -e '
    binmode STDOUT, ":encoding(UTF-8)";
    binmode STDERR, ":encoding(UTF-8)";
    $SIG{PIPE} = "IGNORE";

    sub read_qstring {
      my ($data, $offset) = @_;
      return (undef, $offset) if $offset + 4 > length($data);
      my $length = unpack("N", substr($data, $offset, 4));
      $offset += 4;
      return (undef, $offset) if $length == 0xffffffff || $offset + $length > length($data);
      return (decode("UTF-16BE", substr($data, $offset, $length)), $offset + $length);
    }

    sub qstring {
      my $encoded = encode("UTF-16BE", $_[0] // "");
      return pack("N", length($encoded)) . $encoded;
    }

    sub write_all {
      my ($client, $payload) = @_;
      my $written = 0;
      while ($written < length($payload)) {
        my $n = syswrite($client, $payload, length($payload) - $written, $written);
        return 0 unless defined $n;
        $written += $n;
      }
      return 1;
    }

    sub print_success {
      my ($response) = @_;
      my ($message) = read_qstring($response, 2);
      return 0 unless defined $message && length($message);
      print $message;
      print "\n" unless substr($message, -1) eq "\n";
      return 0;
    }

    my $shell_qml_arg = shift @ARGV;
    my $shell_qml = abs_path($shell_qml_arg);
    my $target = shift @ARGV;
    my $function = shift @ARGV;
    my @args = @ARGV;
    my $runtime_dir = $ENV{"XDG_RUNTIME_DIR"} || "/run/user/$<";
    my $payload = chr(3) . qstring($target) . qstring($function) . pack("N", scalar @args) . join("", map { qstring($_) } @args);
    my @candidates;

    if (!defined $shell_qml) {
      print STDERR "omarchy-shell config not found: $shell_qml_arg\n";
      exit 1;
    }

    for my $lock_path (glob("$runtime_dir/quickshell/by-id/*/instance.lock")) {
      my $data;
      next unless open(my $lock, "<:raw", $lock_path);
      { local $/; $data = <$lock>; }
      close($lock);
      my (undef, $offset) = read_qstring($data, 0);
      my ($path) = read_qstring($data, $offset);
      next unless $path;
      my $abs_path = abs_path($path);
      next unless defined $abs_path && $abs_path eq $shell_qml;
      (my $socket_path = $lock_path) =~ s{/instance\.lock$}{/ipc.sock};
      push @candidates, [(stat($lock_path))[9] || 0, $socket_path];
    }

    for my $candidate (sort { $b->[0] <=> $a->[0] } @candidates) {
      socket(my $client, AF_UNIX, SOCK_STREAM, 0) || next;
      if (connect($client, sockaddr_un($candidate->[1]))) {
        unless (write_all($client, $payload)) {
          close($client);
          next;
        }

        my $response = "";
        while (1) {
          my $buffer = "";
          my $n = sysread($client, $buffer, 65536);
          last unless defined $n && $n > 0;
          $response .= $buffer;
        }
        close($client);

        next unless length($response);
        my $code = ord(substr($response, 0, 1));
        if ($code == 5) {
          print_success($response);
          exit 0;
        } elsif ($code == 2) {
          print STDERR "Target not found.\n";
        } elsif ($code == 3) {
          print STDERR "Function not found.\n";
        } elsif ($code == 4) {
          print STDERR "Invalid arguments.\n";
        } else {
          print STDERR "Unexpected IPC response: $code\n";
        }
        exit 1;
      }
      close($client);
    }

    print STDERR "omarchy-shell is not running\n";
    exit 1;
  ' "$SHELL_QML" "$@"
