package net.filebot;

import static java.util.Arrays.*;
import static java.util.stream.Collectors.*;
import static net.filebot.Execute.*;
import static net.filebot.Logging.*;
import static net.filebot.UserFiles.*;
import static net.filebot.util.FileUtilities.*;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;

import com.sun.jna.Platform;

public enum StandardRenameAction implements RenameAction {

	MOVE {

		@Override
		public File rename(File from, File to) throws Exception {
			return moveRename(from, to);
		}
	},

	COPY {

		@Override
		public File rename(File from, File to) throws Exception {
			return copyAs(from, to);
		}
	},

	KEEPLINK {

		@Override
		public File rename(File from, File to) throws Exception {
			// move file
			File dest = MOVE.rename(from, to);

			// symlink file back into the original location
			SYMLINK.rename(dest, from);

			return dest;
		}
	},

	SYMLINK {

		@Override
		public File rename(File from, File to) throws Exception {
			return createRelativeSymlink(resolveDestination(from, to), from, true);
		}
	},

	HARDLINK {

		@Override
		public File rename(File from, File to) throws Exception {
			return createHardLinkStructure(resolveDestination(from, to), from);
		}
	},

	CLONE {

		@Override
		public File rename(File from, File to) throws Exception {
			File dest = resolveDestination(from, to);

			// clonefile or reflink requires filesystem that supports copy-on-write (e.g. apfs or btrfs)
			if (Platform.isMac()) {
				// -c copy files using clonefile
				system("cp", "-c", "-f", from.getPath(), dest.getPath());
			} else {
				// --reflink copy files using reflink
				system("cp", "--reflink=auto", "--force", from.isDirectory() ? "--recursive" : "--no-target-directory", from.getPath(), dest.getPath());
			}

			return dest;
		}
	},

	DUPLICATE {

		@Override
		public File rename(File from, File to) throws Exception {
			// try to hardlink
			try {
				return HARDLINK.rename(from, to);
			} catch (Exception e) {
				debug.finest(cause(HARDLINK, e));
			}

			// copy if necessary
			return COPY.rename(from, to);
		}
	},

	TEST {

		@Override
		public File rename(File from, File to) throws IOException {
			return resolve(from, to);
		}

		@Override
		public boolean canRevert() {
			return false;
		}
	};

	public String getDisplayName() {
		switch (this) {
		case MOVE:
			return "Rename";
		case COPY:
			return "Copy";
		case KEEPLINK:
			return "Keeplink";
		case SYMLINK:
			return "Symlink";
		case HARDLINK:
			return "Hardlink";
		case CLONE:
			return "Clone";
		case DUPLICATE:
			return "Hardlink or Copy";
		default:
			return "Test";
		}
	}

	public String getDisplayVerb() {
		switch (this) {
		case MOVE:
			return "Moving";
		case COPY:
			return "Copying";
		case KEEPLINK:
			return "Moving and symlinking";
		case SYMLINK:
			return "Symlinking";
		case HARDLINK:
			return "Hardlinking";
		case CLONE:
			return "Cloning";
		case DUPLICATE:
			return "Duplicating";
		default:
			return "Testing";
		}
	}

	public static List<String> names() {
		return stream(values()).map(Enum::name).collect(toList());
	}

	public static StandardRenameAction forName(String name) {
		for (StandardRenameAction action : values()) {
			if (action.name().equalsIgnoreCase(name)) {
				return action;
			}
		}

		throw new IllegalArgumentException(String.format("%s not in %s", name, names()));
	}

	public static File revert(File current, File original) throws IOException {
		// do nothing if current and original path is exactly the same
		if (current.equals(original)) {
			return original;
		}

		// reverse move
		if (current.exists() && !original.exists()) {
			return moveRename(current, original);
		}

		BasicFileAttributes currentAttr = Files.readAttributes(current.toPath(), BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
		BasicFileAttributes originalAttr = Files.readAttributes(original.toPath(), BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);

		// reverse symlink
		if (currentAttr.isSymbolicLink() && !originalAttr.isSymbolicLink()) {
			trash(current);
			return original;
		}

		// reverse keeplink
		if (!currentAttr.isSymbolicLink() && originalAttr.isSymbolicLink()) {
			trash(original);
			return moveRename(current, original);
		}

		// reverse copy / hardlink
		if (currentAttr.isRegularFile() && originalAttr.isRegularFile()) {
			trash(current);
			return original;
		}

		// reverse folder copy
		if (currentAttr.isDirectory() && originalAttr.isDirectory()) {
			trash(original);
			return moveRename(current, original);
		}

		throw new IllegalArgumentException(String.format("Cannot revert file: %s => %s", current, original));
	}

}
