package com.airlock.iam.base.misc.io;

import com.airlock.iam.base.misc.exception.ThrowingConsumer;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;

public final class Files {
   private Files() {
   }

   public static void copyFile(File sourceFile, File targetFile) throws IOException {
      java.nio.file.Files.copy(sourceFile.toPath(), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
   }

   public static void replaceFile(File tempFile, File targetFile) throws IOException {
      replaceFile(tempFile.toPath(), targetFile.toPath());
   }

   public static void replaceFile(Path tempFile, Path targetFile) throws IOException {
      tryAtomicMove(tempFile, targetFile);
   }

   private static void tryAtomicMove(Path source, Path target) throws IOException {
      try {
         java.nio.file.Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
      } catch (IOException atomicMoveException) {
         try {
            java.nio.file.Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
         } catch (IOException replaceException) {
            java.nio.file.Files.deleteIfExists(target);
            java.nio.file.Files.move(source, target);
         }
      }
   }

   @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Regular files have parents and file names")
   public static void doAtomicallyViaSiblingTempFile(Path targetFile, ThrowingConsumer<Path, IOException> action) throws IOException {
      Path tempFile = java.nio.file.Files.createTempFile(targetFile.getParent(), targetFile.getFileName().toString(), ".tmp");
      setPosixPermissionsFrom(tempFile, targetFile, "rw-rw-r--");
      action.accept(tempFile);
      replaceFile(tempFile, targetFile);
   }

   private static void setPosixPermissionsFrom(Path targetFile, Path templateFile, String fallbackPermissions) throws IOException {
      boolean isPosix = java.nio.file.Files.getFileAttributeView(targetFile, PosixFileAttributeView.class) != null;
      if (isPosix) {
         Set<PosixFilePermission> permissions = java.nio.file.Files.exists(templateFile)
            ? java.nio.file.Files.getPosixFilePermissions(templateFile)
            : PosixFilePermissions.fromString(fallbackPermissions);
         java.nio.file.Files.setPosixFilePermissions(targetFile, permissions);
      }
   }
}
