var path = require('path');
var once = require('once');
var concat = require('concat-stream');
var octal = require('octal')
var stat = require('./stat');
var xtend = require('xtend');
var errno = require('./errno');

var ROOT = stat({
	type: 'directory',
	mode: octal(777),
	size: 4096
});

var normalize = function(key) {
	key = key[0] === '/' ? key : '/' + key;
	key = path.normalize(key);
	if (key === '/') return key;
	return key[key.length-1] === '/' ? key.slice(0, -1) : key;
};

var prefix = function(key) {
	var depth = key.split('/').length.toString(36);
	return '0000000000'.slice(depth.length)+depth+key;
};

module.exports = function(db) {
	var that = {};

	that.normalize = normalize;

	that.get = function(key, cb) {
		key = normalize(key);
		if (key === '/') return process.nextTick(cb.bind(null, null, ROOT, '/'));
		db.get(prefix(key), {valueEncoding:'json'}, function(err, doc) {
			if (err && err.notFound) return cb(errno.ENOENT(key), null, key);
			if (err) return cb(err, null, key);
			cb(null, stat(doc), key);
		});
	};

	that.writable = function(key, cb) {
		key = normalize(key);
		if (key === '/') return process.nextTick(cb.bind(null, errno.EPERM(key)));
		that.follow(path.dirname(key), function(err, parent) {
			if (err) return cb(err);
			if (!parent.isDirectory()) return cb(errno.ENOTDIR(key));
			cb(null, key);
		});
	};

	that.list = function(key, cb) {
		key = normalize(key);

		var start = prefix(key === '/' ? key : key + '/');
		var keys = db.createKeyStream({start: start, end: start+'\xff'});

		cb = once(cb);

		keys.on('error', cb);
		keys.pipe(concat({encoding:'object'}, function(files) {
			files = files.map(function(file) {
				return file.split('/').pop();
			});

			cb(null, files);
		}));
	};

	var resolve = function(dir, cb) {
		var root = '/';
		var parts = dir.split('/').slice(1);

		var loop = function() {
			that.get(path.join(root, parts.shift()), function(err, doc, key) {
				if (err) return cb(err, doc, dir);
				root = doc.target || key;
				if (!parts.length) return cb(null, doc, key);
				loop();
			});
		};

		loop();
	};

	that.follow = function(key, cb) {
		resolve(normalize(key), function loop(err, doc, key) {
			if (err) return cb(err, null, key);
			if (doc.target) return that.get(doc.target, loop);
			cb(null, stat(doc), key);
		});
	};

	that.update = function(key, opts, cb) {
		that.get(key, function(err, doc, key) {
			if (err) return cb(err);
			if (key === '/') return cb(errno.EPERM(key));
			that.put(key, xtend(doc, opts), cb);
		});
	};

	that.put = function(key, opts, cb) {
		that.writable(key, function(err, key) {
			if (err) return cb(err);
			db.put(prefix(key), stat(opts), {valueEncoding:'json'}, cb);
		});
	};

	that.del = function(key, cb) {
		key = normalize(key);
		if (key === '/') return process.nextTick(cb.bind(null, errno.EPERM(key)));
		db.del(prefix(key), cb);
	};

	return that;
};
