/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 *
 * Copyright 2005-2016 The Kuali Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.sys.batch;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.kuali.kfs.kns.bo.Step;
import org.kuali.kfs.krad.util.GlobalVariables;
import org.kuali.kfs.krad.util.MessageMap;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.sys.context.BatchStepFileDescriptor;
import org.kuali.kfs.sys.exception.ParseException;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * This class allows the batch process semaphore files to be uploaded. Only the .error files are currently configured for uploading.
 * To enable the other file types to be uploaded configure a spring bean for each type supplying the
 * directoryPath, the fileExtension, and fileTypeIdentifier properties
 */
public class SemaphoreInputFileType extends BatchInputFileTypeBase {

    private static Logger LOG = Logger.getLogger(SemaphoreInputFileType.class);

    private String fileTypeIdentifier;

    /**
     * @param principalName      - unused
     * @param parsedFileContents List<String> in which the first line is the name of the Job and the Step (JobName~StepName)
     * @param fileUserIdentifier is not used
     * @return first line of the parsedFileContents which should be in the form jobName~stepName
     * @see org.kuali.kfs.sys.batch.BatchInputFileType#getFileName(String, Object, String)
     */
    @Override
    public String getFileName(String principalName, Object parsedFileContents, String fileUserIdentifier) {
        String fileName = "";

        if (!validate(parsedFileContents)) {
            fileName += "invalidJob" + BatchStepFileDescriptor.STEP_FILE_NAME_SEPARATOR + "invalidStep";
        } else {
            List<String> content = getParsedFileContent(parsedFileContents);

            String firstLine = content.get(0);
            fileName = firstLine;
        }

        return fileName;
    }

    /**
     * @see org.kuali.kfs.sys.batch.BatchInputFileType#getFileTypeIdentifer()
     */
    @Override
    public String getFileTypeIdentifer() {
        return fileTypeIdentifier;
    }

    public void setFileTypeIdentifier(String fileTypeIdentifier) {
        this.fileTypeIdentifier = fileTypeIdentifier;
    }

    /**
     * @see org.kuali.kfs.sys.batch.BatchInputType#getTitleKey()
     */
    @Override
    public String getTitleKey() {
        return KFSKeyConstants.Semaphore.MESSAGE_BATCH_UPLOAD_TITLE_PREFIX + "." + getFileExtension();
    }

    /**
     * Returns a List<String> in which the first line is the Step and all subsequent lines are considered part of the error message.
     *
     * @see org.kuali.kfs.sys.batch.BatchInputFileType#parse(byte[])
     */
    @Override
    public Object parse(byte[] fileByteContent) throws ParseException {
        BufferedReader bufferedFileReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(fileByteContent)));
        List<String> content = new ArrayList<String>();
        try {
            String fileLine = "";
            while ((fileLine = bufferedFileReader.readLine()) != null) {
                content.add(fileLine);
            }
        } catch (Exception e) {
            LOG.error("An unexpected error occurred while parsing the file: ", e);
            throw new ParseException("An unexpected error occurred while parsing the file: " + e.getMessage(), e);
        }

        return content;
    }

    /**
     * Remove the done file from the file system if it exists. The done file is not used by the batch process.
     *
     * @param fileName           the absolute path, file name, and extension of the file
     * @param parsedFileContents
     * @see org.kuali.kfs.sys.batch.BatchInputFileType#process(String, Object)
     */
    @Override
    public void process(String fileName, Object parsedFileContents) {
        //remove done file
        File doneFile = generateDoneFileObject(fileName);
        if (doneFile.exists()) {
            LOG.info("Removing the .done file for " + fileName);
            if (!doneFile.delete()) {
                LOG.error("Unable to delete the .done file for " + fileName);
                MessageMap errors = new MessageMap();
                errors.putError(KFSConstants.GLOBAL_ERRORS, KFSKeyConstants.Semaphore.ERROR_BATCH_UPLOAD_DELETE_DONE_FILE);
                GlobalVariables.mergeErrorMap(errors);
            }
        }
    }

    /**
     * Validate that the first line of the file contains a job name and a valid Step (the bean id)
     * in the format jobName.stepName
     *
     * @see org.kuali.kfs.sys.batch.BatchInputFileType#validate(Object)
     */
    @Override
    public boolean validate(Object parsedFileContents) {
        List<String> content = getParsedFileContent(parsedFileContents);

        if (content.size() <= 0) {
            MessageMap errors = new MessageMap();
            errors.putError(KFSConstants.GLOBAL_ERRORS, KFSKeyConstants.Semaphore.ERROR_BATCH_UPLOAD_INVALID_STEP);
            GlobalVariables.mergeErrorMap(errors);
            return false;
        }

        String firstLine = content.get(0);
        String stepName = StringUtils.substring(firstLine, StringUtils.lastIndexOf(firstLine, BatchStepFileDescriptor.STEP_FILE_NAME_SEPARATOR) + 1);

        Step step = BatchSpringContext.getStep(stepName);
        if (step == null) {
            LOG.error("Unable to find bean for step: " + stepName);
            MessageMap errors = new MessageMap();
            errors.putError(KFSConstants.GLOBAL_ERRORS, KFSKeyConstants.Semaphore.ERROR_BATCH_UPLOAD_INVALID_STEP);
            GlobalVariables.mergeErrorMap(errors);
            return false;
        }

        return true;
    }

    /**
     * The author principal name is not added to the name of the file or in the file contents
     *
     * @return an empty String
     * @see org.kuali.kfs.sys.batch.BatchInputFileType#getAuthorPrincipalName(File)
     */
    @Override
    public String getAuthorPrincipalName(File file) {
        return "";
    }

    /**
     * This method is responsible for creating a File object that represents the done file. The real file represented on disk may
     * not exist
     *
     * @param batchInputFileName
     * @return a File object representing the done file. The real file may not exist on disk, but the return value can be used to
     * create that file.
     */
    private File generateDoneFileObject(String batchInputFileName) {
        String doneFileName = StringUtils.substringBeforeLast(batchInputFileName, ".") + ".done";
        File doneFile = new File(doneFileName);
        return doneFile;
    }

    @SuppressWarnings("unchecked")
    private List<String> getParsedFileContent(Object parsedFileContent) {
        List<String> content = (List<String>) parsedFileContent;
        return content;
    }
}
