/**
 * Copyright (c) 2014-2018 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see http://www.gnu.org/licenses/
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.bc.ftp.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import org.apache.ftpserver.ftplet.Authority;
import org.apache.ftpserver.ftplet.FtpException;
import org.apache.ftpserver.usermanager.impl.BaseUser;
import org.apache.ftpserver.usermanager.impl.WritePermission;
import org.junit.Rule;
import org.ow2.petals.bc.ftp.FtpProvideExtFlowStepBeginLogData;
import org.ow2.petals.commons.log.FlowLogData;
import org.ow2.petals.commons.log.TraceCode;
import org.ow2.petals.component.framework.junit.TestMessageExchangeFactory;
import org.ow2.petals.component.framework.junit.impl.mock.MockEndpointDirectory;
import org.ow2.petals.component.framework.junit.impl.mock.TestMessageExchangeFactoryImpl;
import org.ow2.petals.component.framework.logger.ProvideExtFlowStepEndLogData;
import org.ow2.petals.component.framework.logger.ProvideExtFlowStepFailureLogData;
import org.ow2.petals.junit.rules.ftpserver.FTPServer;
import org.ow2.petals.junit.rules.log.handler.InMemoryLogHandler;

/**
 * Abtract class for unit tests of {@link FTPService}.
 * 
 * @author Christophe DENEUX - Linagora
 * 
 */
public class AbstractFTPServiceTest {

    /**
     * Name of a user registered on the FTP server
     */
    private static final String USERNAME = "user";

    /**
     * Password of the user {@value #USERNAME}
     */
    private static final String USERPWD = "pwd";

    /**
     * A FTP server for unit test purposes
     */
    @Rule
    public final FTPServer ftpServer = new FTPServer();

    /**
     * A in-memory log handler to track log traces
     */
    @Rule
    public final InMemoryLogHandler inMemoryLogHandler = new InMemoryLogHandler();

    protected final TestMessageExchangeFactory factory = new TestMessageExchangeFactoryImpl(new MockEndpointDirectory(),
            Logger.getAnonymousLogger());

    /**
     * Register a user on the FTP server
     * 
     * @return The user registered
     * @throws FtpException
     *             An error occurs registering the user
     */
    protected final BaseUser registerUserOnFtpServer() throws FtpException {
        final BaseUser user = new BaseUser();
        user.setName(USERNAME);
        user.setPassword(USERPWD);
        final List<Authority> authorities = new ArrayList<Authority>();
        authorities.add(new WritePermission());
        user.setAuthorities(authorities);
        final File homeDirectory = new File(this.ftpServer.getRootFileSystem(), user.getName());
        user.setHomeDirectory(homeDirectory.getAbsolutePath());
        this.ftpServer.registerUser(user);

        return user;
    }

    /**
     * Assert the MONIT trace {@link ProvideExtFlowStepEndLogData}
     * 
     * @param flowInstanceId
     *            The flow instance identifier of the context in which the MONIT trace takes place.
     * @param flowStepId
     *            The flow step identifier of the context in which the MONIT trace takes place.
     * @param logRecord
     *            The {@link LogRecord} to assert.
     */
    // TODO: Move this method into the Petals JUnit Framework
    protected static void assertProvideExtFlowStepEndLogData(final String flowInstanceId, final String flowStepId,
            final LogRecord logRecord) {

        assertNotNull(logRecord);

        assertEquals(1, logRecord.getParameters().length);
        assertTrue(logRecord.getParameters()[0] instanceof ProvideExtFlowStepEndLogData);

        final ProvideExtFlowStepEndLogData logData = (ProvideExtFlowStepEndLogData) logRecord.getParameters()[0];
        assertEquals(3, logData.size());
        assertEquals(TraceCode.PROVIDE_EXT_FLOW_STEP_END, logData.get(FlowLogData.FLOW_STEP_TRACE_CODE));
        assertEquals(flowInstanceId, logData.get(FlowLogData.FLOW_INSTANCE_ID_PROPERTY_NAME));
        assertEquals(flowStepId, logData.get(FlowLogData.FLOW_STEP_ID_PROPERTY_NAME));
    }

    /**
     * Assert the MONIT trace {@link ProvideExtFlowStepFailureLogData}
     * 
     * @param flowInstanceId
     *            The flow instance identifier of the context in which the MONIT trace takes place.
     * @param flowStepId
     *            The flow step identifier of the context in which the MONIT trace takes place.
     * @param logRecord
     *            The {@link LogRecord} to assert.
     */
    // TODO: Move this method into the Petals JUnit Framework
    protected static void assertProvideExtFlowStepFailureLogData(final String flowInstanceId, final String flowStepId,
            final LogRecord logRecord) {

        assertNotNull(logRecord);

        assertEquals(1, logRecord.getParameters().length);
        assertTrue(logRecord.getParameters()[0] instanceof ProvideExtFlowStepFailureLogData);

        final ProvideExtFlowStepFailureLogData logData = (ProvideExtFlowStepFailureLogData) logRecord.getParameters()[0];
        assertEquals(4, logData.size());
        assertEquals(TraceCode.PROVIDE_EXT_FLOW_STEP_FAILURE, logData.get(FlowLogData.FLOW_STEP_TRACE_CODE));
        assertEquals(flowInstanceId, logData.get(FlowLogData.FLOW_INSTANCE_ID_PROPERTY_NAME));
        assertEquals(flowStepId, logData.get(FlowLogData.FLOW_STEP_ID_PROPERTY_NAME));
        final String errorMessage = (String) logData
                .get(ProvideExtFlowStepFailureLogData.FLOW_STEP_FAILURE_MESSAGE_NAME);
        assertNotNull(errorMessage);
        assertFalse(errorMessage.trim().isEmpty());
    }

    /**
     * Assert the MONIT trace {@link FtpProvideExtFlowStepBeginLogData}
     * 
     * @param flowInstanceId
     *            The flow instance identifier of the context in which the MONIT trace takes place.
     * @param flowPreviousStepId
     *            The flow previous step identifier of the context in which the MONIT trace takes place.
     * @param folder
     *            The folder, as detail of the MONIT trace
     * @param patterns
     *            The regexp pattern, as detail of the MONIT trace. Can be <code>null</code>, in this case, no check is
     *            done on the patterns
     * @param logRecord
     *            The {@link LogRecord} to assert.
     * @return The flow step identifier of the context in which the MONIT trace takes place.
     */
    protected static String assertFtpProvideExtFlowStepBeginLogData(final String flowInstanceId,
            final String flowPreviousStepId, final String folder, final String patterns, final LogRecord logRecord) {

        assertNotNull(logRecord);

        assertEquals(1, logRecord.getParameters().length);
        assertTrue(logRecord.getParameters()[0] instanceof FtpProvideExtFlowStepBeginLogData);

        final FtpProvideExtFlowStepBeginLogData logData = (FtpProvideExtFlowStepBeginLogData) logRecord.getParameters()[0];
        assertEquals(patterns == null ? 5 : 6, logData.size());
        assertEquals(TraceCode.PROVIDE_EXT_FLOW_STEP_BEGIN, logData.get(FlowLogData.FLOW_STEP_TRACE_CODE));
        assertEquals(flowInstanceId, logData.get(FlowLogData.FLOW_INSTANCE_ID_PROPERTY_NAME));
        assertNotNull(logData.get(FlowLogData.FLOW_STEP_ID_PROPERTY_NAME));
        assertNotEquals(flowPreviousStepId, logData.get(FlowLogData.FLOW_STEP_ID_PROPERTY_NAME));
        assertEquals(flowPreviousStepId, logData.get(FlowLogData.FLOW_PREVIOUS_STEP_ID_PROPERTY_NAME));
        if (patterns != null) {
            assertTrue("Unexpected patterns: " + logData.get(FtpProvideExtFlowStepBeginLogData.PATTERNS_PROPERTY_NAME),
                    ((String) logData.get(FtpProvideExtFlowStepBeginLogData.PATTERNS_PROPERTY_NAME)).matches(patterns));
        }
        assertEquals(folder, logData.get(FtpProvideExtFlowStepBeginLogData.FOLDER_PROPERTY_NAME));

        return (String) logData.get(FlowLogData.FLOW_STEP_ID_PROPERTY_NAME);
    }
}
