/**
 * Copyright © 2024 Nevis Security AG. All rights reserved.
 */

import uuid from 'react-native-uuid';

import { DeviceInformationMismatch } from './DeviceInformationMismatch';
import { DeviceInformationSyncResult } from './DeviceInformationSyncResult';
import { DeviceInformationSyncPlatformOperation } from '../../cache/operation/DeviceInformationSyncPlatformOperation';
import { PlatformOperationCache } from '../../cache/PlatformOperationCache';
import NevisMobileAuthenticationSdkReact from '../../MobileAuthenticationSdk';
import { DeviceInformationSyncResultMessage } from '../../model/messages/in/DeviceInformationSyncResultMessage';
import { DeviceInformationSyncMessage } from '../../model/messages/out/DeviceInformationSyncMessage';
import { HttpOperation, HttpOperationImpl } from '../HttpOperation';

/**
 * The operation that can be executed to correct the {@link DeviceInformationMismatch} problems
 * that were found in a {@link DeviceInformationCheck}.
 *
 * The operation works in a best-effort mode: it will try to resolve as many mismatches as
 * possible and will report the errors that occurred (if any) through the {@link DeviceInformationSyncResult.errors}
 * method.
 *
 * **WARNING** \
 * Some of the changes result in removal of credentials on both the server and client side. Use this
 * class with caution and understanding its implications. The changes that will be applied are
 * described on the documentation of each {@link DeviceInformationMismatch}.
 *
 * This is supported only when the backend uses nevisFIDO 7.2408.** or later.
 *
 * Usage example:
 * ```ts
 *   [...]
 *   async fixDeviceInformationMismatches(
 *       operations: Operations,
 *       mismatches: DeviceInformationMismatch[]
 *   ): Promise<void> {
 *       operations.deviceInformationSync
 *           .mismatches(mismatches)
 *           .onResult((result) => {
 *               // handle the result
 *           })
 *           .execute();
 *   }
 *   [...]
 * ```
 *
 * @see
 * - {@link DeviceInformationCheck}
 * - {@link DeviceInformationMismatch}
 * - {@link Operations.deviceInformationSync}
 */
export abstract class DeviceInformationSync extends HttpOperation<DeviceInformationSync> {
	/**
	 * Specifies the configuration mismatches to be fixed.
	 *
	 * **IMPORTANT** \
	 * Providing the {@link mismatches} is required.
	 *
	 * @param mismatches the configuration mismatches
	 * @return a {@link DeviceInformationSync} object.
	 */
	abstract mismatches(mismatches: Array<DeviceInformationMismatch>): DeviceInformationSync;

	/**
	 * Specifies the object that will be invoked when the operation completes.
	 *
	 * **IMPORTANT** \
	 * Providing the {@link onResult} callback is required.
	 *
	 * @param onResult the callback that will be invoked when the operation completes.
	 * @return a {@link DeviceInformationSync} object.
	 */
	abstract onResult(
		onResult: (result: DeviceInformationSyncResult) => void
	): DeviceInformationSync;
}

export class DeviceInformationSyncImpl
	extends HttpOperationImpl<DeviceInformationSync>
	implements DeviceInformationSync
{
	private _mismatches?: Array<DeviceInformationMismatch>;
	private _onResult?: (result: DeviceInformationSyncResult) => void;

	constructor() {
		super();
	}

	mismatches(mismatches: Array<DeviceInformationMismatch>): DeviceInformationSync {
		this._mismatches = mismatches;
		return this;
	}

	onResult(onResult: (result: DeviceInformationSyncResult) => void): DeviceInformationSync {
		this._onResult = onResult;
		return this;
	}

	async execute(): Promise<void> {
		const operationId = uuid.v4() as string;
		const operation = new DeviceInformationSyncPlatformOperation(operationId, this._onResult);

		PlatformOperationCache.getInstance().put(operation);

		const message = new DeviceInformationSyncMessage(
			operationId,
			this._onResult !== undefined,
			this._mismatches,
			this.httpRequestHeaders
		);

		function finish() {
			PlatformOperationCache.getInstance().delete(operationId);
		}

		return NevisMobileAuthenticationSdkReact.deviceInformationSync(message).then(
			(result: DeviceInformationSyncResultMessage) => {
				const resultMessage = DeviceInformationSyncResultMessage.fromJson(result);
				operation.handleResult(resultMessage.result);
				finish();
			}
		);
	}
}
