/*
 * Copyright 2015-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.integration.ftp;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.ftpserver.FtpServer;
import org.apache.ftpserver.FtpServerFactory;
import org.apache.ftpserver.ftplet.Authentication;
import org.apache.ftpserver.ftplet.AuthenticationFailedException;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.ftplet.User;
import org.apache.ftpserver.ftplet.UserManager;
import org.apache.ftpserver.listener.Listener;
import org.apache.ftpserver.listener.ListenerFactory;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.ConcurrentLoginPermission;
import org.apache.ftpserver.usermanager.impl.TransferRatePermission;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.junit.AfterClass;
import org.junit.BeforeClass;

import org.springframework.integration.file.remote.RemoteFileTestSupport;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.ftp.server.ApacheMinaFtplet;
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;

/**
 * Provides an embedded FTP Server for test cases.
 *
 * @author Artem Bilan
 * @author Gary Russell
 * @author David Turanski
 * @since 4.3
 */
public class FtpTestSupport extends RemoteFileTestSupport {

	private static final ApacheMinaFtplet FTPLET = new ApacheMinaFtplet();

	private static volatile FtpServer server;

	@BeforeClass
	public static void createServer() throws Exception {
		FtpServerFactory serverFactory = new FtpServerFactory();
		serverFactory.setUserManager(new TestUserManager(remoteTemporaryFolder.getRoot().getAbsolutePath()));

		ListenerFactory factory = new ListenerFactory();
		factory.setPort(0);
		serverFactory.addListener("default", factory.createListener());
		serverFactory.setFtplets(new HashMap<>(Collections.singletonMap("springFtplet", FTPLET)));
		FTPLET.setApplicationEventPublisher(ev -> {
			// no-op
		});
		server = serverFactory.createServer();
		server.start();

		Listener listener = serverFactory.getListeners().values().iterator().next();
		port = listener.getPort();
	}


	@AfterClass
	public static void stopServer() throws Exception {
		server.stop();
	}

	@Override
	protected String prefix() {
		return "ftp";
	}

	public static SessionFactory<FTPFile> sessionFactory() {
		return new CachingSessionFactory<FTPFile>(rawSessionFactory());
	}

	protected static DefaultFtpSessionFactory rawSessionFactory() {
		DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
		sf.setHost("localhost");
		sf.setPort(port);
		sf.setUsername("foo");
		sf.setPassword("foo");
		sf.setClientMode(FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE);
		return sf;
	}

	protected static ApacheMinaFtplet ftplet() {
		return FTPLET;
	}

	private static class TestUserManager implements UserManager {

		private final BaseUser testUser;

		private TestUserManager(String homeDirectory) {
			this.testUser = new BaseUser();
			this.testUser.setAuthorities(Arrays.asList(new ConcurrentLoginPermission(1024, 1024),
					new WritePermission(),
					new TransferRatePermission(1024, 1024)));
			this.testUser.setHomeDirectory(homeDirectory);
			this.testUser.setName("TEST_USER");
		}


		@Override
		public User getUserByName(String s) throws FtpException {
			return this.testUser;
		}

		@Override
		public String[] getAllUserNames() throws FtpException {
			return new String[] { "TEST_USER" };
		}

		@Override
		public void delete(String s) throws FtpException {
		}

		@Override
		public void save(User user) throws FtpException {
		}

		@Override
		public boolean doesExist(String s) throws FtpException {
			return true;
		}

		@Override
		public User authenticate(Authentication authentication) throws AuthenticationFailedException {
			return this.testUser;
		}

		@Override
		public String getAdminName() throws FtpException {
			return "admin";
		}

		@Override
		public boolean isAdmin(String s) throws FtpException {
			return s.equals("admin");
		}

	}

}
