/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
 */

import { asyncExec, randomNumber } from "./utils";
import { CloudDBZoneQueryPolicy } from "./enums";
import {
    CloudDBZoneConfig,
    CloudDBZoneSnapshot,
    CloudDBZoneSubscribeSnapshot,
    QueryObject,
    Transaction,
    TransactionResult
} from "./interfaces";

export default class AGCCloudDBZone {
    private readonly _id: string;

    constructor(id: string) {
        this._id = id;
    }

    /**
     * Obtains the id of current Cloud DB zone instance.
     */
    get id(): string {
        return this._id;
    }

    /**
     * Obtains the current configuration information of Cloud DB zone.
     * @returns A CloudDBZoneConfig object.
     */
    getCloudDBZoneConfig(): Promise<CloudDBZoneConfig> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["getCloudDBZoneConfig", this.id]);
    }

    /**
     * Writes a group of objects to the current Cloud DB zone in batches.
     * @param className Name of the object type.
     * @param cloudDBZoneObjectArray Object datas to be written.
     * @returns Number of objects that are successfully written.
     */
    executeUpsert(className: string, cloudDBZoneObjectArray: any[]): Promise<number> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["executeUpsert", className, cloudDBZoneObjectArray, this._id]);
    }

    /**
     * Deletes a group of Cloud DB zone objects whose primary key values are the same as those of the input object list in batches.
     * @param className Name of the object type.
     * @param cloudDBZoneObjectArray Object datas to be deleted.
     * @returns Number of objects that are successfully deleted.
     */
    executeDelete(className: string, cloudDBZoneObjectArray: any[]): Promise<number> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["executeDelete", className, cloudDBZoneObjectArray, this._id]);
    }

    /**
     * Queries objects that meet specified conditions in Cloud DB zone and encapsulates the query result set into a CloudDBZoneSnapshot.
     * @param queryObject A QueryObject object, which indicates the query condition.
     * @param queryPolicy Query policy, which specifies the data source to be queried.
     * @returns CloudDBZoneSnapshot object that encapsulates the query result set.
     */
    executeQuery(queryObject: QueryObject, queryPolicy: CloudDBZoneQueryPolicy): Promise<CloudDBZoneSnapshot> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["executeQuery", queryObject, queryPolicy, this._id]);
    }

    /**
     * Queries objects that meet specified conditions in Cloud DB zone and returns the average value of specified fields.
     * @param queryObject A QueryObject object, which indicates the query condition.
     * @param fieldName Specifies the name of the fields whose average value needs to be calculated in the query object.
     * @param queryPolicy Query policy, which specifies the data source to be queried.
     * @returns The avarage value.
     */
    executeAverageQuery(queryObject: QueryObject, fieldName: string, queryPolicy: CloudDBZoneQueryPolicy): Promise<string> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["executeAverageQuery", queryObject, fieldName, queryPolicy, this._id]);
    }

    /**
     * Queries objects that meet specific conditions and returns the sum of data record values of specified fields in the Cloud DB zone.
     * @param queryObject A QueryObject object, which indicates the query condition.
     * @param fieldName Specifies the name of the fields whose sum value needs to be calculated in the query object.
     * @param queryPolicy Query policy, which specifies the data source to be queried.
     * @returns The sum result.
     */
    executeSumQuery(queryObject: QueryObject, fieldName: string, queryPolicy: CloudDBZoneQueryPolicy): Promise<string> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["executeSumQuery", queryObject, fieldName, queryPolicy, this._id]);
    }

    /**
     * Queries the objects that meet specific conditions and returns the maximum value of the data records in the specified fields in the Cloud DB zone.
     * @param queryObject A QueryObject object, which indicates the query condition.
     * @param fieldName Specifies the name of the fields whose max value needs to be calculated in the query object.
     * @param queryPolicy Query policy, which specifies the data source to be queried.
     * @returns The maximum value.
     */
    executeMaximumQuery(queryObject: QueryObject, fieldName: string, queryPolicy: CloudDBZoneQueryPolicy): Promise<string> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["executeMaximumQuery", queryObject, fieldName, queryPolicy, this._id]);
    }

    /**
     * Queries the objects that meet specific conditions in the Cloud DB zone and returns the minimum value of the data records in the designated fields.
     * @param queryObject A QueryObject object, which indicates the query condition.
     * @param fieldName Specifies the name of the fields whose min value needs to be calculated in the query object.
     * @param queryPolicy Query policy, which specifies the data source to be queried.
     * @returns The minimum value.
     */
    executeMinimalQuery(queryObject: QueryObject, fieldName: string, queryPolicy: CloudDBZoneQueryPolicy): Promise<string> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["executeMinimalQuery", queryObject, fieldName, queryPolicy, this._id]);
    }

    /**
     * Queries the objects that meet specific conditions in the Cloud DB zone and returns the number of data records of the specified fields.
     * @param queryObject A QueryObject object, which indicates the query condition.
     * @param fieldName Specifies the name of the fields whose count value needs to be calculated in the query object.
     * @param queryPolicy Query policy, which specifies the data source to be queried.
     * @returns The number of data result.
     */
    executeCountQuery(queryObject: QueryObject, fieldName: string, queryPolicy: CloudDBZoneQueryPolicy): Promise<string> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["executeCountQuery", queryObject, fieldName, queryPolicy, this._id]);
    }

    /**
     * Queries all object data that meets specified conditions in Cloud DB but has not been synchronized to the cloud, and encapsulates the query result set into a CloudDBZoneSnapshot.
     * @param queryObject A QueryObject object, which indicates the query condition.
     * @returns CloudDBZoneSnapshot object that encapsulates the query result set.
     */
    executeQueryUnsynced(queryObject: QueryObject): Promise<CloudDBZoneSnapshot> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["executeQueryUnsynced", queryObject, this._id]);
    }

    /**
     * Executes a specified transaction operation.
     * @param transactions You can encapsulate a group of operations in the array, 
     * and then use this interface to implement the functions of adding, deleting, modifying, 
     * and querying operations in a transaction.
     * @returns An object, which encapsulates the execution status, result, 
     * and exception information of runTransaction(). 
     * For example, you can call the isSuccessful property based on this 
     * object to check whether the query operation is successful.
     */
    runTransaction(transactions: Transaction[]): Promise<TransactionResult> {
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["runTransaction", transactions, this._id]);
    }

    /**
     * Registers a listener for a specified object on the device or cloud. 
     * When the data of the object that is listened on is changed, the registered onSnapshotUpdate event is triggered.
     * @param queryObject A QueryObject object, which indicates the query condition.
     * @param queryPolicy Query policy, which specifies the data source to be queried.
     * @param callback Callback function to be called when listener is triggered.
     * @returns A ListenerHandler object that remains valid until the remove() method 
     * of the ListenerHandler class is explicitly called by the developer.
     */
    async subscribeSnapshot(queryObject: QueryObject, queryPolicy: CloudDBZoneQueryPolicy, callback: (cloudDBZoneSnapshot: CloudDBZoneSubscribeSnapshot) => void): Promise<ListenerHandler> {
        let listenerId = randomNumber().toString();
        window.subscribeAGCEvent("onSnapshotUpdate" + this._id + listenerId, callback);
        try {
            await asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["subscribeSnapshot", queryObject, queryPolicy, this._id, listenerId]);
        } catch (error) {
            delete window.AGCEvents["onSnapshotUpdate" + this._id + listenerId];
            throw error;
        }
        return new ListenerHandler(this._id, listenerId);
    }
}

class ListenerHandler {
    private readonly zoneId: string;
    private readonly listenerId: string;

    constructor(zoneId: string, listenerId: string) {
        this.zoneId = zoneId;
        this.listenerId = listenerId;
    }

    /**
     * Deregisters the snapshot listener.
     */
    remove(): Promise<void> {
        delete window.AGCEvents["onSnapshotUpdate" + this.zoneId + this.listenerId];
        return asyncExec('AGCCloudDBPlugin', "CloudDBZoneService", ["unsubscribeSnapshot", this.zoneId, this.listenerId]);
    }
}
