const fs = require('fs');
const path = require('path');

/**
 * @class PathRetriever
 */
class PathRetriever {

    /**
     * Find first possible existing path for the given settings
     *
     * @static
     * @param {Object} settings Path Seek Settings Object
     * @param {string} [settings.target=''] Target resource path
     * @param {string|Array<string>} [settings.ext='json'] Mock target extension to find
     * @param {string} prefix Mock prefix: {PATH}.{PREFIX}.{EXT}
     * @param {string} [cwd=process.cwd()] Current Working Directory: Path to seek for resources
     * @returns {string} Existing resource path
     * @memberof PathRetriever
     */
    static find({ target = '', ext = 'json', prefix, cwd = process.cwd() }){
        const possiblePaths = this.seekPathList({target, ext, prefix});
        for (const relativeTargetPath of possiblePaths) {
            const targetPath = path.join(cwd, relativeTargetPath);
            if (fs.existsSync(targetPath)) {
                return targetPath;
            }
        }
        return null;
    }

    /**
     * @static
     * @property {string} DEFAULT_RESOURCE
     * @memberof PathRetriever
     */
    static get DEFAULT_RESOURCE() {
        return 'default';
    }

    /**
     * Uri path resource matcher
     *
     * @static
     * @property {RegExp} PATH_RESOURCE_MATCHER
     * @memberof PathRetriever
     */
    static get PATH_RESOURCE_MATCHER() {
        return /([^/]+)/g;
    }

    /**
     * Returns a list of all possible paths, for the given resource, sorted by priority
     *
     * @static
     * @param {Array<string>} relativePath Target resource path array
     * @returns {Array<Array<string>>} List of possible paths
     * @memberof PathRetriever
     */
    static *partialSearch(pathArray) {
        if (!Array.isArray(pathArray)) {
            throw new Error(`pathArray should be an Array, ${pathArray} provided`);
        }
        if (pathArray.length > 1) {
            const subPathArray = pathArray.slice(0, -1);
            const subSearchResults = this.partialSearch(subPathArray);
            const cachedSubSearchResults = [];
            const targetResource = pathArray.slice(-1);

            //Yielding subSearchResults with target resource ending restored
            for (const subPath of subSearchResults) {
                yield [...subPath, targetResource];
                cachedSubSearchResults.push(subPath);
            }

            //Yielding cached subSearchResults with default resource ending replaced
            for (const subPath of cachedSubSearchResults) {
                yield [...subPath, this.DEFAULT_RESOURCE];
            }
        } else {
            //Yielding pathArray
            yield pathArray;
            //Yielding pathArray with default resource ending replaced
            yield [this.DEFAULT_RESOURCE];
        }
    }

    /**
     * Returns a list of all possible paths, for the given resource settings, sorted by priority
     *
     * @static
     * @param {Object} settings Path Seek Settings Object
     * @param {string} settings.target Target resource path
     * @param {string|Array<string>} [settings.ext='json'] Mock target extension to find
     * @param {string} prefix Mock prefix: {PATH}.{PREFIX}.{EXT}
     * @returns {Array<string>} List of possible paths
     * @memberof PathRetriever
     */
    static *seekPathList({ target, ext = 'json', prefix }) {
        if (!target) {
            throw new Error('target path is mandatory');
        }

        const extList = [].concat(ext);
        const pathArray = target.match(this.PATH_RESOURCE_MATCHER);
        const partialResults = this.partialSearch(pathArray);

        for (const pathLikelihood of partialResults) {
            const pathLikelihoodStr =  pathLikelihood.join('/');
            if (prefix) {
                for (const currentExt of extList) {
                    yield `${pathLikelihoodStr}.${prefix}.${currentExt}`;
                }
            }
            for (const currentExt of extList) {
                yield `${pathLikelihoodStr}.${currentExt}`;
            }
        }
    }
}

exports.PathRetriever = PathRetriever;