"use strict";

const Promise = require("bluebird");
const chokidar = require("chokidar");
const createFileSource = require("./file-source");
const fs = Promise.promisifyAll(require("fs"));
const path = require("path");

const splitPath = require("./split-path");

/* FIXME: Add deletion propagation support, based on correlating input files to output files */
module.exports = function watch(pattern, options = {}) {
	let watcher;
	let basePath;

	if (options.basePath != null) {
		/* FIXME: Verify that, if an explicit basePath is specified, it's not deeper in the path than the automatically-determined one (for any of the specified patterns); otherwise file-paths-relative-to-the-base-path no longer make any sense. */
		basePath = options.basePath;
	} else {
		if (typeof pattern === "string") {
			let pathSegments = splitPath(pattern);

			let firstWildcardSegment = pathSegments.findIndex((segment) => {
				return segment.includes("*");
			});

			if (firstWildcardSegment === -1) {
				/* Assume an exact file path, and treat its folder as the base folder */
				basePath = path.dirname(pattern);
			} else {
				/* Assume a path containing a wildcard, and treat the last non-wildcard segment as the base folder */
				basePath = pathSegments.slice(0, firstWildcardSegment).join(path.sep);
			}
		} else {
			throw new Error("When specifying multiple patterns to watch, you must explicitly specify a basePath in the options");
		}
	}

	return createFileSource({
		displayName: `Watch for changes (${pattern})`,
		supportsTeardown: true,
		basePath: basePath,
		initialize: ({options, resolvePath, pushFile, reportError}) => {
			function push(filePath, stats) {
				return Promise.try(() => {
					/* FIXME: Define more sensible resolvePath semantics */
					let resolvedPath = resolvePath(filePath);

					let metadata = {
						isVirtual: false,
						path: resolvedPath,
						lastModified: stats.mtime
					};

					/* FIXME: File object format validation */
					if (options.supportsStreams) {
						pushFile({
							metadata: {
								isStream: true,
								... metadata
							},
							stream: fs.createReadStream(resolvedPath)
						});
					} else {
						return Promise.try(() => {
							return fs.readFileAsync(resolvedPath);
						}).then((contents) => {
							pushFile({
								metadata: {
									isStream: false,
									... metadata
								},
								contents: contents
							});
						});
					}
				}).catch((err) => {
					reportError(err, false);
				});
			}

			watcher = chokidar.watch(pattern, {alwaysStat: true})
				.on("add", (path, stats) => {
					push(path, stats);
				})
				.on("change", (path, stats) => {
					push(path, stats);
				})
				.on("error", (err) => {
					/* FIXME: Determine whether the error is fatal */
					reportError(err, false);
				});
		},
		teardown: function () {
			watcher.close();
		}
	});
};
