// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// CRURegistration is an Objective-C library intended to be statically compiled
// into MacOS software that wants to use the Chromium Updater. It is a wrapper
// for updater command line tools. It manages an operation queue internally, so
// callers are not required to wait for results between requests to the updater.

#ifndef CHROME_UPDATER_MAC_CLIENT_LIB_CRUREGISTRATION_H_
#define CHROME_UPDATER_MAC_CLIENT_LIB_CRUREGISTRATION_H_

#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>

/** C macros for brand-specific constants. In the .h.in version of this file,
  * the real values will be inserted via the `apply_updater_branding` step. In
  * the .h version, they have already been interpolated. */
#define CRU_COMPANY_SHORTNAME_STRING "@COMPANY_SHORTNAME@"
#define CRU_KEYSTONE_NAME "@KEYSTONE_NAME@"
#define CRU_PRODUCT_FULLNAME_STRING "@PRODUCT_FULLNAME@"

NS_ASSUME_NONNULL_BEGIN

/**
 * The name of the `.zip` file, excluding the actual `.zip` extension, that
 * CRURegistration must extract to find the updater. This must refer to a file
 * that is packaged as a Resource in the bundle using CRURegistration.
 */
extern NSString* const CRUArchiveBasename;

/**
 * The name of the updater `.app` and binary itself (inside the `.app` in its
 * ordinary location), without any extensions, which CRURegistration runs after
 * extracting the updater to install it.
 */
extern NSString* const CRUInstallerAppBasename;

/** The domain for user or system errors reported by CRURegistration. */
extern NSString* const CRURegistrationErrorDomain;

/**
 * The domain for internal errors from CRURegistration. Clients should never
 * encounter these; please file a bug if you get errors in this domain.
 */
extern NSString* const CRURegistrationInternalErrorDomain;

/**
 * NSError userInfo dict key mapped to the POSIX errno for NSErrors in
 * CRURegistrationErrorDomain with underlying POSIX causes.
 */
extern NSString* const CRUErrnoKey;

/**
 * NSError userInfo dict key mapped to the string captured from the stderr
 * output of a command-line tool invoked by CRURegistration. This error is
 * usually intended to be human-readable, rather than machine-readable.
 */
extern NSString* const CRUStderrKey;

/**
 * NSError userInfo dict key mapped to the string captured from the stdout
 * output of a command-line tool invoked by CRURegistration. May contain partial
 * or incorrect output from the command that failed.
 */
extern NSString* const CRUStdoutKey;

/**
 * NSError userInfo dict key mapped to the return code of a failing task (as
 * an NSNumber wrapping NSInteger).
 */
extern NSString* const CRUReturnCodeKey;

typedef NS_ERROR_ENUM(CRURegistrationErrorDomain, CRURegistrationError){
    /**
     * `CRURegistration` couldn't read a stream (stdout or stderr) when running
     * a subprocess. The POSIX error code for the error is available in the
     * error's user data under `CRUErrnoKey`.
     */
    CRURegistrationErrorTaskStreamUnreadable = 1,

    /**
     * `CRURegistration` couldn't find the updater or the updater installer.
     */
    CRURegistrationErrorHelperNotFound = 2,

    /**
     * The updater component invoked by `CRURegistration` returned an error. Its
     * error message is available in the error's user data under `CRUStderrKey`.
     * Any partial output, usable for debugging purposes, is available under
     * `CRUStdoutKey`.
     */
    CRURegistrationErrorTaskFailed = 3,

    /**
     * Parameters for a CRURegistration operation were invalid. Details about
     * the invalid parameters are available in the error's `userInfo` dict
     * under `NSDebugDescriptionErrorKey`.
     *
     * Invalid arguments may also show up as `CRURegistrationErrorTaskFailed`,
     * if they are detected by the underlying helper binary instead of
     * `CRURegistration` itself. Whenever `CRURegistration` replies with this
     * error, it also fails an `NSAssert` so the issue can be discovered
     * in debug builds while the faulty call is still on the stack.
     */
    CRURegistrationErrorInvalidArgument = 4,

    /**
     * A macOS API failed in a way that implies that important parts of the
     * filesystem are not usable for the requested operation, which
     * `CRURegistration` was running in-process. The issue is something
     * different from "could not find the updater".
     *
     * File system errors encountered while trying to find a helper to run a
     * task out-of-process are reported as `CRURegistrationErrorHelperNotFound`.
     * Errors encountered by such helpers are always reported as
     * `CRURegistrationErrorTaskFailed` instead.
     */
    CRURegistrationErrorFilesystem = 5,

    /**
     * CRURegistration could not find an updater package inside the bundle's
     * resources. Is the updater packaged correctly, under the expected name,
     * and included as a resource in the bundle? The expected name is
     * the value provided by CRUArchiveBasename with ".zip" appended to it.
     * If you have different files providing different values of
     * CRUArchiveBasename for different build scenarios, verify whether the
     * build configuration is correctly matching updater archive flavors to
     * CRURegistrationPaths (or similar) variations.
     *
     * NSFilePathErrorKey contains the value of CRUArchiveBasename, with
     * ".zip" appended, matching the name of the expected resource file that
     * CRURegistration could not find.
     */
    CRURegistrationErrorUpdaterArchiveNotFound = 6,
};

/**
 * CRURegistration interfaces with Chromium Updater to configure and retrieve
 * information about an app, or to install the updater for the current user. Its
 * methods can be invoked from any thread or queue.
 *
 * Do not block CRURegistration's target queue synchronously waiting for a
 * callback from CRURegistration; this causes deadlock. Invoking CRURegistration
 * methods on this (or any) queue without subsequently synchronously waiting for
 * a provided callback to execute is safe. CRURegistration does not block its
 * target queue.
 */
@interface CRURegistration : NSObject

/**
 * Initializes a CRURegistration instance to manage Chromium Updater's
 * information about the app with the provided ID, using a specified queue
 * for execution and callbacks. This queue can be serial or concurrent, but
 * typically should not be the main queue.
 *
 * @param appId The ID of the app this CRURegistration instance operates on.
 * @param xcPath The absolute path of the application bundle for the instance
 *     of the app this CRURegistration operates on, or another path the updater
 *     registers to verify the existence of an app on disk.
 * @param targetQueue Dispatch queue for callbacks and internal operations.
 *     If this queue is blocked, CRURegistration will get stuck.
 */
- (instancetype)initWithAppId:(NSString*)appId
         existenceCheckerPath:(NSString*)xcPath
                  targetQueue:(dispatch_queue_t)targetQueue
    NS_DESIGNATED_INITIALIZER;

/**
 * Initializes a CRURegistration instance to manage Chromium Updater's
 * information about the app with the provided ID, using a global concurrent
 * queue for execution (with the specified quality of service).
 *
 * @param appId The ID of the app this CRURegistration instance operates on.
 * @param xcPath The absolute path of the application bundle for the instance
 *     of the app this CRURegistration operates on, or another path the updater
 *     registers to verify the existence of an app on disk.
 * @param qos Identifier for the global concurrent queue to use for callbacks
 *     and internal operations. See Apple's documentation for
 *     `dispatch_get_global_queue` for more details:
 *     https://developer.apple.com/documentation/dispatch/1452927-dispatch_get_global_queue
 */
- (instancetype)initWithAppId:(NSString*)appId
         existenceCheckerPath:(NSString*)xcPath
                          qos:(dispatch_qos_class_t)qos;

/**
 * Initializes a CRURegistration instance to manage Chromium Updater's
 * information about the app with the provided ID, using the `QOS_CLASS_UTILITY`
 * global concurrent queue for execution.
 *
 * @param appId The ID of the app this CRURegistration instance operates on.
 * @param xcPath The absolute path of the application bundle for the instance
 *     of the app this CRURegistration operates on, or another path the updater
 *     registers to verify the existence of an app on disk.
 */
- (instancetype)initWithAppId:(NSString*)appId
         existenceCheckerPath:(NSString*)xcPath;

/**
 * CRURegistration cannot be initialized without an app ID and existence
 * checker path.
 */
- (instancetype)init NS_UNAVAILABLE;

/**
 * Asynchronously registers the app for updates, owned by the current user.
 *
 * If the app is already registered for the current user, this updates the
 * registration.
 *
 * When registration has completed, `reply` will be dispatched to the target
 * queue. If registration succeeds, the `NSError*` argument will be nil.
 * If it does not, the `NSError*` argument to `reply` will contain a
 * descriptive error.
 *
 * `reply` may be nil if the application is not interested in the result of
 * registration.
 */
- (void)registerVersion:(NSString*)version
                  reply:(void (^_Nullable)(NSError* _Nullable))reply;

/**
 * Asynchronously retrieves the tag for the registered app.
 *
 * `reply` will be dispatched to the target queue when tag reading has completed
 * or failed. If the tag can be read successfully, it will be passed as the
 * NSString* argument of the reply block and the NSError* argument will be nil.
 * If the tag cannot be read, the NSString* will be nil while the NSError* will
 * contain a descriptive error.
 *
 * If the tag is empty, the NSString* argument will be the empty string (and
 * there is no error). If no app with the provided ID is registered or the
 * updater is not installed, an error occurs.
 */
- (void)fetchTagWithReply:(void (^)(NSString* _Nullable,
                                    NSError* _Nullable))reply;

/**
 * Asynchronously installs the updater, as the current user.
 *
 * `reply` will be dispatched to the target queue when installation has
 * completed or failed. If the installation is skipped because there is already
 * an updater present at the same or higher version, it is considered to have
 * completed successfully.
 *
 * If installation succeeds (or is skipped), the `NSError*` argument to `reply`
 * will be nil. If it fails, the argument will contain a descriptive error.
 */
- (void)installUpdaterWithReply:(void (^_Nullable)(NSError* _Nullable))reply;

/**
 * Asynchronously requests the updater to immediately check for updates for the
 * registered app.
 *
 * `reply` will be dispatched to the target queue when the request has been
 * processed or failed. If the update check was successful, the `NSError*`
 * argument to `reply` will be nil. If an update is available, its version
 * will be passed as the `version` argument.
 *
 * If the check succeeds but no update is available, both arguments will be nil.
 * If it fails to start or run, the `error` argument will contain a descriptive
 * error.
 */
- (void)checkForUpdateWithReply:(void (^_Nullable)(NSString* _Nullable version,
                                                   NSError* _Nullable error))reply;

/**
 * Asynchronously marks the current product active for the current user.
 *
 * This may run concurrently with other operations performed by CRURegistration
 * and return out-of-order, although it will still run under, and reply on,
 * the target queue. `reply` will be dispatched to the target queue when the
 * "active" file for the provided app ID has been written (or the attempt to
 * do so has failed).
 */
- (void)markActiveWithReply:(void (^_Nullable)(NSError* _Nullable))reply;
@end

NS_ASSUME_NONNULL_END

#endif  // CHROME_UPDATER_MAC_CLIENT_LIB_CRUREGISTRATION_H_
