package com.airlock.iam.servicecontainer.app.application.configuration.task;

import com.airlock.iam.core.misc.logging.LogFileLimiter;
import com.airlock.iam.core.misc.logging.TimestampFormat;
import com.airlock.iam.elog.api.ELogger;
import com.airlock.iam.elog.api.ELoggerFactory;
import com.airlock.iam.plugin.api.annotation.config.ConfigurationValidator;
import com.airlock.iam.plugin.api.annotation.config.DeprecatedPlugin;
import com.airlock.iam.plugin.api.annotation.config.Plugin;
import com.airlock.iam.plugin.api.annotation.property.EnumProperty;
import com.airlock.iam.plugin.api.annotation.property.FileProperty;
import com.airlock.iam.plugin.api.annotation.property.IntegerProperty;
import com.airlock.iam.plugin.api.annotation.property.StringListProperty;
import com.airlock.iam.plugin.api.config.ConfigurationContext;
import com.airlock.iam.plugin.api.config.ConfigurationEnvironment;
import com.airlock.iam.plugin.api.config.Environment;
import com.airlock.iam.plugin.api.config.LogTypes;
import com.airlock.iam.plugin.api.config.PluginConfiguration;
import com.airlock.iam.plugin.api.exception.ConfigurationException;
import com.airlock.iam.plugin.api.exception.PropertyConstraintViolationException;
import com.airlock.iam.plugin.api.valuereader.StringListPropertyValueReader;
import com.airlock.iam.servicecontainer.api.application.configuration.task.TaskConfig;
import com.airlock.iam.servicecontainer.api.domain.model.task.AbstractTask;
import com.airlock.iam.servicecontainer.api.domain.model.task.Task;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.File;
import java.util.List;

@Plugin(
   description = "<p>Deletes the oldest log files periodically so that only a configurable number of log files are kept. This can be used to limit log file growth.</p>\n<p>Log file timestamps are extracted from the log file names in order to determine which ones to delete.</p>"
)
@DeprecatedPlugin(
   description = "<p>This service task is deprecated, as it has been superseded by the more flexible Log4J rollover configuration.</p>\n<p>Consider updating your custom Log4J config with a &lt;TimeBasedTriggeringPolicy&gt;, in combination with either a\n&lt;DefaultRolloverStrategy&gt; or a &lt;DirectWriteRolloverStrategy&gt; to perform the automatic log file cleanup.</p>\n<p>Note that all IAM log files are set up to use Log4J and Tomcat Webserver rollover strategies by default.</p>"
)
public class LogCleanupTask extends AbstractTask implements TaskConfig {
   private static final ELogger LOG = ELoggerFactory.getLogger(LogCleanupTask.class);
   @IntegerProperty(
      description = "<p>The number of log files to keep.</p>\n<p>If there are more log files, the oldest ones will be deleted, such that only the configured number of log files remain.</p>",
      exampleValues = {30, 60, 90},
      minValue = 1,
      optional = false
   )
   private Integer numberOfLogFilesToKeep;
   @FileProperty(
      description = "The directory in which the log files are stored.",
      isDirectory = true,
      assureExistence = true,
      assureReadable = true,
      optional = false,
      exampleValues = "instances/XYZ/logs"
   )
   private File logDirectory;
   @StringListProperty(
      description = "<p>Log file path pattern to determine which logs to clean up.</p>\n<p>The oldest log files that match an expression will be deleted so that for every entry only the configured\nnumber of log files remain. Use <code>$(timestamp)</code> to mark the location of the log file timestamps.</p>\n<p>Files that don't match any pattern will be completely ignored by this task.</p>",
      optional = true,
      defaultValue = {"loginapp.log.$(timestamp)", "adminapp.log.$(timestamp)", "servicecontainerapp.log.$(timestamp)", "medusa-audit.log.$(timestamp)"},
      exampleElements = "mylogfile.log.$(timestamp)"
   )
   private List<String> logFileNamePatterns;
   @EnumProperty(
      description = "<p>The format of the timestamp in the log file names.</p>\n<p>Timestamps are marked by $(timestamp) and are use to determine the oldest log files, which are deleted first.</p>\n<ul>\n<li><code>yyyy</code> stands for the year.</li>\n<li><code>MM</code> stands for the month.</li>\n<li><code>dd</code> stands for the day.</li>\n</ul>",
      defaultValue = "DEFAULT",
      optional = true
   )
   private TimestampFormat timestampFormat;

   @ConfigurationValidator
   static void validateConfiguration(PluginConfiguration config, ConfigurationEnvironment env, ConfigurationContext context, Environment validationEnvironment) throws ConfigurationException {
      String propertyName = "logFileNamePatterns";

      for (String pattern : StringListPropertyValueReader.effectiveStringListConfigValue(config, propertyName, env, context)) {
         if (!pattern.contains("$(timestamp)")) {
            String description = "The log file name pattern \""
               + pattern
               + "\" is mising a timestamp marker. Every file name pattern must contain this maker: $(timestamp)";
            throw new PropertyConstraintViolationException(propertyName, config, env, context, description);
         }
      }
   }

   @Override
   protected void executeInternal() {
      for (String fileNamePattern : this.logFileNamePatterns) {
         try {
            LogFileLimiter limiter = new LogFileLimiter(this.logDirectory, fileNamePattern, this.timestampFormat, this.numberOfLogFilesToKeep);
            limiter.cleanupLogFiles();
         } catch (RuntimeException e) {
            LOG.warn(LogTypes.CONFIGURATION_ERROR, "Can't clean-up log files with pattern " + fileNamePattern + ". " + e.getMessage());
         }
      }
   }

   @Override
   public Task getLogic() {
      return this;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static LogCleanupTask.LogCleanupTaskBuilder builder() {
      return new LogCleanupTask.LogCleanupTaskBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public LogCleanupTask() {
   }

   @SuppressFBWarnings(justification = "generated code")
   public LogCleanupTask(Integer numberOfLogFilesToKeep, File logDirectory, List<String> logFileNamePatterns, TimestampFormat timestampFormat) {
      this.numberOfLogFilesToKeep = numberOfLogFilesToKeep;
      this.logDirectory = logDirectory;
      this.logFileNamePatterns = logFileNamePatterns;
      this.timestampFormat = timestampFormat;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class LogCleanupTaskBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private Integer numberOfLogFilesToKeep;
      @SuppressFBWarnings(justification = "generated code")
      private File logDirectory;
      @SuppressFBWarnings(justification = "generated code")
      private List<String> logFileNamePatterns;
      @SuppressFBWarnings(justification = "generated code")
      private TimestampFormat timestampFormat;

      @SuppressFBWarnings(justification = "generated code")
      LogCleanupTaskBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public LogCleanupTask.LogCleanupTaskBuilder numberOfLogFilesToKeep(Integer numberOfLogFilesToKeep) {
         this.numberOfLogFilesToKeep = numberOfLogFilesToKeep;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LogCleanupTask.LogCleanupTaskBuilder logDirectory(File logDirectory) {
         this.logDirectory = logDirectory;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LogCleanupTask.LogCleanupTaskBuilder logFileNamePatterns(List<String> logFileNamePatterns) {
         this.logFileNamePatterns = logFileNamePatterns;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LogCleanupTask.LogCleanupTaskBuilder timestampFormat(TimestampFormat timestampFormat) {
         this.timestampFormat = timestampFormat;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public LogCleanupTask build() {
         return new LogCleanupTask(this.numberOfLogFilesToKeep, this.logDirectory, this.logFileNamePatterns, this.timestampFormat);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "LogCleanupTask.LogCleanupTaskBuilder(numberOfLogFilesToKeep="
            + this.numberOfLogFilesToKeep
            + ", logDirectory="
            + this.logDirectory
            + ", logFileNamePatterns="
            + this.logFileNamePatterns
            + ", timestampFormat="
            + this.timestampFormat
            + ")";
      }
   }
}
