// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.sync;

import com.yahoo.config.provision.ApplicationId;
import com.yahoo.vespa.hosted.node.admin.task.util.file.UnixPath;

import java.net.URI;
import java.nio.file.Path;
import java.time.Duration;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * @author freva
 */
public class SyncFileInfo {

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter
            .ofPattern("yyyy-MM-dd.HH-mm-ss").withZone(ZoneOffset.UTC);

    private final Path source;
    private final URI destination;
    private final Compression uploadCompression;
    private final Map<String, String> tags;
    private final Optional<Duration> minDurationBetweenSync;

    private SyncFileInfo(Path source, URI destination, Compression uploadCompression,
                         Map<String, String> tags, Duration minDurationBetweenSyncOrNull) {
        this.source = source;
        this.destination = destination;
        this.uploadCompression = uploadCompression;
        this.tags = Map.copyOf(tags);
        this.minDurationBetweenSync = Optional.ofNullable(minDurationBetweenSyncOrNull);
    }

    /** Source path of the file to sync */
    public Path source() {
        return source;
    }

    /** Remote URI to store the file at */
    public URI destination() {
        return destination;
    }

    /** Compression algorithm to use when uploading the file */
    public Compression uploadCompression() {
        return uploadCompression;
    }

    public Map<String, String> tags() { return tags; }

    public Optional<Duration> minDurationBetweenSync() { return minDurationBetweenSync; }

    public static Optional<SyncFileInfo> forLogFile(URI uri, Path logFile, boolean rotatedOnly, ApplicationId owner) {
        String filename = logFile.getFileName().toString();
        Compression compression;
        String dir = null;
        String remoteFilename = logFile.getFileName().toString();
        Duration minDurationBetweenSync = null;

        if (filename.startsWith("vespa.log")) {
            dir = "logs/vespa/";
            compression = Compression.ZSTD;
            minDurationBetweenSync = filename.length() == 9 ? rotatedOnly ? Duration.ofHours(1) : Duration.ZERO : null;
        } else if (filename.startsWith("zookeeper.") && filename.endsWith(".log")) {
            compression = Compression.ZSTD;
            dir = "logs/zookeeper/";
            remoteFilename = filename.endsWith(".0.log") ? "zookeeper.log" :
                    "zookeeper.log-" + DATE_TIME_FORMATTER.format(new UnixPath(logFile).getLastModifiedTime());
            minDurationBetweenSync = filename.endsWith(".0.log") ? rotatedOnly ? Duration.ofHours(1) : Duration.ZERO : null;
        } else {
            compression = filename.endsWith(".zst") ? Compression.NONE : Compression.ZSTD;
            if (rotatedOnly && compression != Compression.NONE)
                dir = null;
            else if (filename.startsWith("JsonAccessLog.") || filename.startsWith("access"))
                dir = "logs/access/";
            else if (filename.startsWith("ConnectionLog."))
                dir = "logs/connection/";
        }

        if (dir == null) return Optional.empty();
        return Optional.of(new SyncFileInfo(
                logFile, uri.resolve(dir + remoteFilename + compression.extension), compression, defaultTags(owner),
                minDurationBetweenSync));
    }

    public static SyncFileInfo forServiceDump(URI destinationDir, Path file, Compression compression,
                                              ApplicationId owner, String assetClassification) {
        String filename = file.getFileName().toString();
        URI location = destinationDir.resolve(filename + compression.extension);
        Map<String, String> tags = defaultTags(owner);
        if (assetClassification != null) {
            tags.put("vespa:AssetClassification", assetClassification);
        }
        return new SyncFileInfo(file, location, compression, tags, null);
    }

    private static Map<String, String> defaultTags(ApplicationId owner) {
        var tags = new HashMap<String, String>();
        tags.put("corp:Application", owner.toFullString());
        return tags;
    }

    public enum Compression {
        NONE(""), ZSTD(".zst");

        private final String extension;
        Compression(String extension) {
            this.extension = extension;
        }
    }
}

@@ -496,16 +496,52 @@
 ptional;
+%0Aimport java.util.function.Function;
 %0A%0A/**%0A *
@@ -792,20 +792,38 @@
 te final
+ Function%3CString,
  URI
+%3E
  destina
@@ -1020,20 +1020,38 @@
  source,
+ Function%3CString,
  URI
+%3E
  destina
@@ -1631,24 +1631,238 @@
  destination
+.apply(%22%22);%0A    %7D%0A%0A    /** Returns a destination URI after adding a suffix to the base name of the filename. */%0A    public URI destinationWithBasenameSuffix(String suffix) %7B%0A        return destination.apply(suffix)
 ;%0A    %7D%0A%0A   
@@ -2371,24 +2371,30 @@
 on;%0A        
+final 
 String dir =
@@ -2387,31 +2387,24 @@
 l String dir
- = null
 ;%0A        St
@@ -3662,24 +3662,82 @@
 nnection/%22;%0A
+            else%0A                return Optional.empty();%0A
         %7D%0A%0A 
@@ -3797,69 +3797,106 @@
     
-return Optional.of(new SyncFileInfo(%0A                logFile,
+String finalRemoteFilename = remoteFilename;%0A        Function%3CString, URI%3E destination = suffix -%3E
  uri
@@ -3910,17 +3910,22 @@
 e(dir + 
-r
+finalR
 emoteFil
@@ -3923,32 +3923,41 @@
 RemoteFilename +
+ suffix +
  compression.ext
@@ -3963,16 +3963,82 @@
 tension)
+;%0A        return Optional.of(new SyncFileInfo(logFile, destination
 , compre
@@ -4063,32 +4063,16 @@
 (owner),
-%0A               
  minDura
@@ -4360,20 +4360,38 @@
 %0A       
+ Function%3CString,
  URI
+%3E
  locatio
@@ -4389,24 +4389,34 @@
 %3E location =
+ suffix -%3E
  destination
@@ -4437,16 +4437,25 @@
 lename +
+ suffix +
  compres
@@ -4912,24 +4912,24 @@
 lString());%0A
-
         retu
@@ -4936,32 +4936,131 @@
 rn tags;%0A    %7D%0A%0A
+    public boolean overwriteIfExists() %7B%0A        return minDurationBetweenSync.isPresent();%0A    %7D%0A%0A
     public enum 

Allow suffix of base name of filename for upload URI