/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.apache.atlas.notification.spool;

import org.apache.atlas.notification.spool.models.IndexRecord;
import org.apache.atlas.notification.spool.models.IndexRecords;
import org.apache.atlas.type.AtlasType;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;

public class SpoolUtils {
    private static final Logger LOG = LoggerFactory.getLogger(SpoolUtils.class);

    public static final  String DEFAULT_CHAR_SET               = "UTF-8";
    public static final  String FILE_EXT_LOG                   = ".log";
    private static final String USER_SPECIFIC_PATH_NAME_FORMAT = "%s-%s";
    private static final String DEFAULT_LINE_SEPARATOR         = System.lineSeparator();
    private static final String FILE_EXT_JSON                  = ".json";
    private static final String SPOOL_FILE_NAME_FORMAT_PREFIX  = "%s.%s%s";
    private static final String INDEX_FILE_CLOSED_SUFFIX       = "_closed.json";
    private static final String INDEX_FILE_PUBLISH_SUFFIX      = "_publish.json";
    private static final String INDEX_FILE_NAME_FORMAT         = "index-%s-%s" + FILE_EXT_JSON;
    private static final String SPOOL_FILE_NAME_FORMAT         = "spool-%s-%s-%s" + FILE_EXT_LOG;
    private static final String RECORD_EMPTY                   = StringUtils.leftPad(StringUtils.EMPTY, IndexRecord.RECORD_SIZE) + SpoolUtils.getLineSeparator();

    private SpoolUtils() {
        // to block instantiation
    }

    public static File getCreateFile(File file, String source) throws IOException {
        if (createFileIfNotExists(file, source)) {
            LOG.info("SpoolUtils.getCreateFile(source={}): file={}", source, file.getAbsolutePath());
        }

        return file;
    }

    public static boolean createFileIfNotExists(File file, String source) throws IOException {
        boolean ret = file.exists();

        if (!ret) {
            ret = file.createNewFile();

            if (!ret) {
                LOG.error("SpoolUtils.createFileIfNotExists(source={}): error creating file {}", source, file.getPath());
            }
        }

        return ret;
    }

    public static File getCreateDirectoryWithPermissionCheck(File file, String user) {
        File ret = getCreateDirectory(file);

        LOG.info("SpoolUtils.getCreateDirectory({}): Checking permissions...", file);

        if (!file.canWrite() || !file.canRead()) {
            File fileWithUserSuffix = getFileWithUserSuffix(file, user);

            LOG.error("SpoolUtils.getCreateDirectory({}, {}): Insufficient permissions for user: {}! Will create: {}", file.getAbsolutePath(), user, user, fileWithUserSuffix);

            ret = getCreateDirectory(fileWithUserSuffix);
        }

        return ret;
    }

    public static File getCreateDirectory(File file) {
        File ret = file;

        if (!file.isDirectory()) {
            boolean result = file.mkdirs();

            if (!file.isDirectory() || !result) {
                LOG.error("SpoolUtils.getCreateDirectory({}): cannot be created!", file);

                ret = null;
            }
        }

        return ret;
    }

    public static PrintWriter createAppendPrintWriter(File filePath) throws UnsupportedEncodingException, FileNotFoundException {
        return new PrintWriter(
                new BufferedWriter(
                        new OutputStreamWriter(
                                new FileOutputStream(filePath, true), DEFAULT_CHAR_SET)));
    }

    public static String getIndexFileName(String source, String handlerName) {
        return String.format(SpoolUtils.INDEX_FILE_NAME_FORMAT, source, handlerName);
    }

    public static String getIndexDoneFile(String filePath) {
        return StringUtils.substringBeforeLast(filePath, SpoolUtils.FILE_EXT_JSON) + SpoolUtils.INDEX_FILE_CLOSED_SUFFIX;
    }

    public static String getIndexPublishFile(String filePath) {
        return StringUtils.substringBeforeLast(filePath, SpoolUtils.FILE_EXT_JSON) + SpoolUtils.INDEX_FILE_PUBLISH_SUFFIX;
    }

    public static boolean fileExists(IndexRecord record) {
        return record != null && new File(record.getPath()).exists();
    }

    public static String getSpoolFilePath(SpoolConfiguration cfg, String spoolDir, String archiveFolder, String suffix) {
        String fileName  = getSpoolFileName(cfg.getSourceName(), cfg.getMessageHandlerName(), suffix);
        int    lastDot   = StringUtils.lastIndexOf(fileName, '.');
        String baseName  = fileName.substring(0, lastDot);
        String extension = fileName.substring(lastDot);
        File   ret;

        for (int sequence = 1; true; sequence++) {
            ret = new File(spoolDir, fileName);

            File archiveLogFile = new File(archiveFolder, fileName);

            if (!ret.exists() && !archiveLogFile.exists()) {
                break;
            }

            fileName = String.format(SPOOL_FILE_NAME_FORMAT_PREFIX, baseName, sequence, extension);
        }

        return ret.getPath();
    }

    public static String getLineSeparator() {
        return DEFAULT_LINE_SEPARATOR;
    }

    public static String getRecordForWriting(IndexRecord record) {
        String json = AtlasType.toJson(record);

        return StringUtils.rightPad(json, IndexRecord.RECORD_SIZE) + SpoolUtils.getLineSeparator();
    }

    public static String getEmptyRecordForWriting() {
        return RECORD_EMPTY;
    }

    public static IndexRecords createRecords(String[] items) {
        IndexRecords records = new IndexRecords();

        if (items != null && items.length > 0) {
            try {
                for (String item : items) {
                    if (StringUtils.isNotBlank(item)) {
                        IndexRecord record = AtlasType.fromJson(item, IndexRecord.class);

                        records.getRecords().put(record.getId(), record);
                    }
                }
            } catch (Exception ex) {
                LOG.error("SpoolUtils.createRecords(): error loading records.", ex);
            }
        }

        return records;
    }

    static String getSpoolFileName(String source, String handlerName, String guid) {
        return String.format(SPOOL_FILE_NAME_FORMAT, source, handlerName, guid);
    }

    private static File getFileWithUserSuffix(File file, String user) {
        if (!file.isDirectory()) {
            return file;
        }

        String absolutePath = file.getAbsolutePath();
        if (absolutePath.endsWith(File.pathSeparator)) {
            absolutePath = StringUtils.removeEnd(absolutePath, File.pathSeparator);
        }

        return new File(String.format(USER_SPECIFIC_PATH_NAME_FORMAT, absolutePath, user));
    }
}
