#!/usr/bin/env python3
"""Send an IPC message to Dynamics via NSDistributedNotificationCenter."""

import argparse
import json
import os
import subprocess
import sys

NOTIFICATION_NAME = "us.zuos.dynamics.ipc.message"
DEFAULT_DYNAMICS_BUNDLE_ID = "us.zuos.dynamics"


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description="Post an IPC message to Dynamics."
    )
    parser.add_argument(
        "--bundle-id",
        default="com.apple.Terminal",
        help="Sender app bundle id (default: com.apple.Terminal).",
    )
    parser.add_argument(
        "--app-name",
        default="Terminal",
        help="Sender app display name.",
    )
    parser.add_argument(
        "--icon-path",
        default="/Applications/Utilities/Terminal.app",
        help="Sender app icon file path or .app bundle path.",
    )
    parser.add_argument(
        "--title",
        default="IPC Test Message",
        help="Message title.",
    )
    parser.add_argument(
        "--detail",
        default="Hello from sample.py",
        help="Message detail text.",
    )
    parser.add_argument(
        "--dynamics-bundle-id",
        default=DEFAULT_DYNAMICS_BUNDLE_ID,
        help=f"Dynamics bundle id to check before posting (default: {DEFAULT_DYNAMICS_BUNDLE_ID}).",
    )
    parser.add_argument(
        "--skip-running-check",
        action="store_true",
        help="Skip checking whether Dynamics is running before posting.",
    )
    return parser


def post_message(payload: dict[str, str], target_bundle_id: str, require_running: bool) -> None:
    swift_script = """
import Foundation
import AppKit

let payloadJSONString = ProcessInfo.processInfo.environment["DYNAMICS_IPC_PAYLOAD"] ?? "{}"
let targetBundleID = ProcessInfo.processInfo.environment["DYNAMICS_TARGET_BUNDLE_ID"] ?? "us.zuos.dynamics"
let requireRunning = (ProcessInfo.processInfo.environment["DYNAMICS_IPC_REQUIRE_RUNNING"] ?? "1") != "0"

let isTargetRunning = !NSRunningApplication.runningApplications(withBundleIdentifier: targetBundleID).isEmpty
if requireRunning && !isTargetRunning {
    fputs("Dynamics is not running (bundle id: \\(targetBundleID)).\\n", stderr)
    exit(3)
}

guard let payloadData = payloadJSONString.data(using: .utf8),
      let object = try? JSONSerialization.jsonObject(with: payloadData),
      let payload = object as? [String: Any] else {
    fputs("Invalid DYNAMICS_IPC_PAYLOAD.\\n", stderr)
    exit(2)
}

DistributedNotificationCenter.default().postNotificationName(
    Notification.Name("__NOTIFICATION_NAME__"),
    object: nil,
    userInfo: payload,
    options: [.deliverImmediately]
)

print("Posted IPC message to Dynamics.")
""".replace("__NOTIFICATION_NAME__", NOTIFICATION_NAME).strip()

    env = dict(os.environ)
    env["DYNAMICS_IPC_PAYLOAD"] = json.dumps(payload, ensure_ascii=False)
    env["DYNAMICS_TARGET_BUNDLE_ID"] = target_bundle_id
    env["DYNAMICS_IPC_REQUIRE_RUNNING"] = "1" if require_running else "0"

    subprocess.run(
        ["xcrun", "swift", "-e", swift_script],
        check=True,
        env=env,
    )


def main() -> int:
    parser = build_parser()
    args = parser.parse_args()

    payload = {
        "senderBundleID": args.bundle_id,
        "appName": args.app_name,
        "appIconPath": args.icon_path,
        "title": args.title,
        "detail": args.detail,
    }

    try:
        post_message(
            payload=payload,
            target_bundle_id=args.dynamics_bundle_id,
            require_running=not args.skip_running_check,
        )
    except subprocess.CalledProcessError as error:
        print(f"Failed to post message: {error}", file=sys.stderr)
        return error.returncode

    print("Payload:")
    print(json.dumps(payload, indent=2, ensure_ascii=False))
    print("If this sender is not approved yet, go to Dynamics Settings > Permissions and enable it.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
