var acorn = require('acorn');
var walk = require('acorn/dist/walk');
var defined = require('defined');

var requireRe = /\brequire\b/;

function parse (src, opts) {
    if (!opts) opts = {};
    return acorn.parse(src, {
        ecmaVersion: defined(opts.ecmaVersion, 9),
        sourceType: opts.sourceType,
        ranges: defined(opts.ranges, opts.range),
        locations: defined(opts.locations, opts.loc),
        allowReserved: defined(opts.allowReserved, true),
        allowReturnOutsideFunction: defined(
            opts.allowReturnOutsideFunction, true
        ),
        allowImportExportEverywhere: defined(
            opts.allowImportExportEverywhere, true
        ),
        allowHashBang: defined(opts.allowHashBang, true)
    });
}

var exports = module.exports = function (src, opts) {
    return exports.find(src, opts).strings;
};

exports.find = function (src, opts) {
    if (!opts) opts = {};
    
    var word = opts.word === undefined ? 'require' : opts.word;
    if (typeof src !== 'string') src = String(src);
    
    var isRequire = opts.isRequire || function (node) {
        return node.callee.type === 'Identifier'
            && node.callee.name === word
        ;
    };
    
    var modules = { strings : [], expressions : [] };
    if (opts.nodes) modules.nodes = [];
    
    var wordRe = word === 'require' ? requireRe : RegExp('\\b' + word + '\\b');
    if (!wordRe.test(src)) return modules;
    
    var ast = parse(src, opts.parse);
    
    function visit(node, st, c) {
        var hasRequire = wordRe.test(src.slice(node.start, node.end));
        if (!hasRequire) return;
        walk.base[node.type](node, st, c);
        if (node.type !== 'CallExpression') return;
        if (isRequire(node)) {
            if (node.arguments.length) {
                var arg = node.arguments[0];
                if (arg.type === 'Literal') {
                    modules.strings.push(arg.value);
                }
                else if (arg.type === 'TemplateLiteral'
                        && arg.quasis.length === 1
                        && arg.expressions.length === 0) {

                    modules.strings.push(arg.quasis[0].value.raw);
                }
                else {
                    modules.expressions.push(src.slice(arg.start, arg.end));
                }
            }
            if (opts.nodes) modules.nodes.push(node);
        }
    }
    
    walk.recursive(ast, null, {
        Statement: visit,
        Expression: visit
    });
    
    return modules;
};
