var util = require('util');
var fs = require('../../util/fs');
var path = require('path');
var mout = require('mout');
var Q = require('q');
var junk = require('junk');
var Resolver = require('./Resolver');
var copy = require('../../util/copy');
var extract = require('../../util/extract');
var createError = require('../../util/createError');

function FsResolver(decEndpoint, config, logger) {
    Resolver.call(this, decEndpoint, config, logger);

    // Ensure absolute path
    this._source = path.resolve(this._config.cwd, this._source);

    // If target was specified, simply reject the promise
    if (this._target !== '*') {
        throw createError(
            "File system sources can't resolve targets",
            'ENORESTARGET'
        );
    }

    // If the name was guessed
    if (this._guessedName) {
        // Remove extension
        this._name = this._name.substr(
            0,
            this._name.length - path.extname(this._name).length
        );
    }
}

util.inherits(FsResolver, Resolver);
mout.object.mixIn(FsResolver, Resolver);

// -----------------

FsResolver.isTargetable = function() {
    return false;
};

// TODO: Should we store latest mtimes in the resolution and compare?
//       This would be beneficial when copying big files/folders

// TODO: There's room for improvement by using streams if the source
//       is an archive file, by piping read stream to the zip extractor
//       This will likely increase the complexity of code but might worth it
FsResolver.prototype._resolve = function() {
    return this._copy()
        .then(this._extract.bind(this))
        .then(this._rename.bind(this));
};

// -----------------

FsResolver.prototype._copy = function() {
    var that = this;

    return Q.nfcall(fs.stat, this._source).then(function(stat) {
        var dst;
        var copyOpts;
        var promise;

        that._sourceStat = stat;
        copyOpts = { mode: stat.mode };

        // If it's a folder
        if (stat.isDirectory()) {
            dst = that._tempDir;

            // Read the bower.json inside the folder, so that we
            // copy only the necessary files if it has ignore specified
            promise = that
                ._readJson(that._source)
                .then(function(json) {
                    copyOpts.ignore = json.ignore;
                    return copy.copyDir(that._source, dst, copyOpts);
                })
                .then(function() {
                    // Resolve to null because it's a dir
                    return;
                });
            // Else it's a file
        } else {
            dst = path.join(that._tempDir, path.basename(that._source));
            promise = copy
                .copyFile(that._source, dst, copyOpts)
                .then(function() {
                    return dst;
                });
        }

        that._logger.action('copy', that._source, {
            src: that._source,
            dst: dst
        });

        return promise;
    });
};

FsResolver.prototype._extract = function(file) {
    if (!file || !extract.canExtract(file)) {
        return Q.resolve();
    }

    this._logger.action('extract', path.basename(this._source), {
        archive: file,
        to: this._tempDir
    });

    return extract(file, this._tempDir);
};

FsResolver.prototype._rename = function() {
    return Q.nfcall(fs.readdir, this._tempDir).then(
        function(files) {
            var file;
            var oldPath;
            var newPath;

            // Remove any OS specific files from the files array
            // before checking its length
            files = files.filter(junk.isnt);

            // Only rename if there's only one file and it's not the json
            if (
                files.length === 1 &&
                !/^(bower|component)\.json$/.test(files[0])
            ) {
                file = files[0];
                this._singleFile = 'index' + path.extname(file);
                oldPath = path.join(this._tempDir, file);
                newPath = path.join(this._tempDir, this._singleFile);

                return Q.nfcall(fs.rename, oldPath, newPath);
            }
        }.bind(this)
    );
};

FsResolver.prototype._savePkgMeta = function(meta) {
    // Store main if is a single file
    if (this._singleFile) {
        meta.main = this._singleFile;
    }

    return Resolver.prototype._savePkgMeta.call(this, meta);
};

module.exports = FsResolver;
