"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2018 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const firebase_functions_1 = require("firebase-functions");
const encoder_1 = require("firebase-functions/lib/encoder");
const firebase_admin_1 = require("firebase-admin");
const lodash_1 = require("lodash");
const app_1 = require("../app");
/** Create a DocumentSnapshot. */
function makeDocumentSnapshot(
/** Key-value pairs representing data in the document, pass in `{}` to mock the snapshot of
 * a document that doesn't exist.
 */
data, 
/** Full path of the reference (e.g. 'users/alovelace') */
refPath, options) {
    let firestoreService;
    let project;
    if (lodash_1.has(options, 'app')) {
        firestoreService = firebase_admin_1.firestore(options.firebaseApp);
        project = lodash_1.get(options, 'app.options.projectId');
    }
    else {
        firestoreService = firebase_admin_1.firestore(app_1.testApp().getApp());
        project = process.env.GCLOUD_PROJECT;
    }
    const resource = `projects/${project}/databases/(default)/documents/${refPath}`;
    const proto = lodash_1.isEmpty(data) ? resource : {
        fields: objectToValueProto(data),
        createTime: encoder_1.dateToTimestampProto(lodash_1.get(options, 'createTime', new Date().toISOString())),
        updateTime: encoder_1.dateToTimestampProto(lodash_1.get(options, 'updateTime', new Date().toISOString())),
        name: resource,
    };
    const readTimeProto = encoder_1.dateToTimestampProto(lodash_1.get(options, 'readTime') || new Date().toISOString());
    return firestoreService.snapshot_(proto, readTimeProto, 'json');
}
exports.makeDocumentSnapshot = makeDocumentSnapshot;
/** Fetch an example document snapshot already populated with data. Can be passed into a wrapped
 * Firestore onCreate or onDelete function.
 */
function exampleDocumentSnapshot() {
    return makeDocumentSnapshot({
        aString: 'foo',
        anObject: {
            a: 'bar',
            b: 'faz',
        },
        aNumber: 7,
    }, 'records/1234');
}
exports.exampleDocumentSnapshot = exampleDocumentSnapshot;
/** Fetch an example Change object of document snapshots already populated with data.
 * Can be passed into a wrapped Firestore onUpdate or onWrite function.
 */
function exampleDocumentSnapshotChange() {
    return firebase_functions_1.Change.fromObjects(makeDocumentSnapshot({
        anObject: {
            a: 'bar',
        },
        aNumber: 7,
    }, 'records/1234'), makeDocumentSnapshot({
        aString: 'foo',
        anObject: {
            a: 'qux',
            b: 'faz',
        },
        aNumber: 7,
    }, 'records/1234'));
}
exports.exampleDocumentSnapshotChange = exampleDocumentSnapshotChange;
/** @internal */
function objectToValueProto(data) {
    const encodeHelper = (val) => {
        if (typeof val === 'string') {
            return {
                stringValue: val,
            };
        }
        if (typeof val === 'boolean') {
            return {
                booleanValue: val,
            };
        }
        if (typeof val === 'number') {
            if (val % 1 === 0) {
                return {
                    integerValue: val,
                };
            }
            return {
                doubleValue: val,
            };
        }
        if (val instanceof Date) {
            return {
                timestampValue: val.toISOString(),
            };
        }
        if (val instanceof Array) {
            let encodedElements = [];
            for (const elem of val) {
                const enc = encodeHelper(elem);
                if (enc) {
                    encodedElements.push(enc);
                }
            }
            return {
                arrayValue: {
                    values: encodedElements,
                },
            };
        }
        if (val === null) {
            return {
                nullValue: 'NULL_VALUE',
            };
        }
        if (val instanceof Buffer || val instanceof Uint8Array) {
            return {
                bytesValue: val,
            };
        }
        if (val instanceof firebase_admin_1.firestore.DocumentReference) {
            const projectId = lodash_1.get(val, '_referencePath.projectId');
            const database = lodash_1.get(val, '_referencePath.databaseId');
            const referenceValue = [
                'projects', projectId,
                'databases', database,
                val.path,
            ].join('/');
            return { referenceValue };
        }
        if (val instanceof firebase_admin_1.firestore.Timestamp) {
            return {
                timestampValue: val.toDate().toISOString(),
            };
        }
        if (lodash_1.isPlainObject(val)) {
            return {
                mapValue: {
                    fields: objectToValueProto(val),
                },
            };
        }
        throw new Error('Cannot encode ' + val + 'to a Firestore Value.' +
            ' Local testing does not yet support Firestore geo points.');
    };
    return lodash_1.mapValues(data, encodeHelper);
}
exports.objectToValueProto = objectToValueProto;
