"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
 * @module metadata
 */
const attribute_names_const_1 = require("../../dynamo/expression/functions/attribute-names.const");
const key_model_const_1 = require("../impl/model/key-model.const");
/**
 * Checks if given metadata returns a sort key when calling metadata.getSortKey
 * @hidden
 */
function hasSortKey(metadata) {
    return metadata.getSortKey() !== null;
}
exports.hasSortKey = hasSortKey;
class Metadata {
    constructor(modelConstructor) {
        this.modelOptions = Reflect.getMetadata(key_model_const_1.KEY_MODEL, modelConstructor);
    }
    static findMetaDataForProperty(modelOpts, propertyName) {
        return modelOpts.properties && modelOpts.properties.find(property => property.name === propertyName || property.nameDb === propertyName) || undefined;
    }
    forProperty(propertyKey) {
        if (!this.modelOptions.properties) {
            return;
        }
        if (typeof propertyKey === 'string' && attribute_names_const_1.NESTED_ATTR_PATH_REGEX.test(propertyKey)) {
            const regex = new RegExp(attribute_names_const_1.NESTED_ATTR_PATH_CAPTURED_REGEX);
            let re;
            let currentMeta = this.modelOptions;
            let lastPropMeta;
            let lastPathPart = '';
            // tslint:disable-next-line:no-conditional-assignment
            while ((re = regex.exec(propertyKey)) !== null) {
                lastPathPart = re[1];
                lastPropMeta = Metadata.findMetaDataForProperty(currentMeta, lastPathPart);
                if (lastPropMeta && lastPropMeta.typeInfo) {
                    currentMeta = new Metadata(lastPropMeta.typeInfo.genericType || lastPropMeta.typeInfo.type).modelOptions;
                }
                else {
                    break;
                }
            }
            if (lastPropMeta && (lastPathPart === lastPropMeta.name || lastPathPart === lastPropMeta.nameDb)) {
                return lastPropMeta;
            }
        }
        else {
            return Metadata.findMetaDataForProperty(this.modelOptions, propertyKey);
        }
        return;
    }
    /**
     *
     * @returns {Array<PropertyMetadata<any>>} Returns all the properties property the @PartitionKeyUUID decorator is present, returns an empty array by default
     */
    getKeysWithUUID() {
        return filterBy(this.modelOptions, p => !!(p.key && p.key.uuid), []);
    }
    /**
     * @param {string} indexName
     * @returns {string} Returns the name of partition key (not the db name if it differs from property name)
     * @throws Throws an error if no partition key was defined for the current model
     * @throws Throws an error if an indexName was delivered but no index was found for given name
     */
    getPartitionKey(indexName) {
        if (indexName) {
            const index = this.getIndex(indexName);
            if (index) {
                return index.partitionKey;
            }
            else {
                throw new Error(`there is no index defined for name ${indexName}`);
            }
        }
        else {
            const property = filterByFirst(this.modelOptions, p => !!(p.key && p.key.type === 'HASH'));
            if (property) {
                return property.name;
            }
            else {
                throw new Error('could not find any partition key');
            }
        }
    }
    /**
     * @param {string} indexName
     * @returns {keyof T} Returns the name of sort key (not the db name if it differs from property name) or null if none was defined
     * @throws Throws an error if an indexName was delivered but no index was found for given name or the found index has no sort key defined
     */
    getSortKey(indexName) {
        if (indexName) {
            const index = this.getIndex(indexName);
            if (index) {
                if (index.sortKey) {
                    return index.sortKey;
                }
                else {
                    throw new Error(`there is no sort key defined for index ${indexName}`);
                }
            }
            else {
                throw new Error(`there is no index defined for name ${indexName}`);
            }
        }
        else {
            const property = filterByFirst(this.modelOptions, p => !!(p.key && p.key.type === 'RANGE'));
            return property ? property.name : null;
        }
    }
    /**
     *
     * @returns {SecondaryIndex[]} Returns all the secondary indexes if exists or an empty array if none is defined
     */
    getIndexes() {
        if (this.modelOptions.indexes && this.modelOptions.indexes.size) {
            return Array.from(this.modelOptions.indexes.values());
        }
        else {
            return [];
        }
    }
    /**
     * @param {string} indexName
     * @returns {SecondaryIndex} Returns the index if one with given name exists, null otherwise
     */
    getIndex(indexName) {
        if (this.modelOptions.indexes) {
            const index = this.modelOptions.indexes.get(indexName);
            return index ? index : null;
        }
        return null;
    }
}
exports.Metadata = Metadata;
/**
 * @hidden
 */
function filterBy(modelOptions, predicate, defaultValue) {
    if (modelOptions && modelOptions.properties) {
        const properties = modelOptions.properties.filter(predicate);
        if (properties && properties.length) {
            return properties;
        }
    }
    return defaultValue;
}
/**
 * @hidden
 */
function filterByFirst(modelOptions, predicate) {
    const properties = filterBy(modelOptions, predicate, null);
    return properties && properties.length ? properties[0] : null;
}
//# sourceMappingURL=metadata.js.map