package com.airlock.iam.application.webserver.infrastructure.deploy;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public final class DirectoryWatcher implements AutoCloseable {
   private final WatchService watchService;
   private final ExecutorService executorService = Executors.newSingleThreadExecutor();

   public DirectoryWatcher(Path directoryToWatch, Runnable eventListener) {
      try {
         this.watchService = FileSystems.getDefault().newWatchService();
         this.registerAllDirectories(Objects.requireNonNull(directoryToWatch));
         this.executorService.execute(() -> {
            WatchKey key;
            do {
               try {
                  key = this.watchService.take();
               } catch (InterruptedException e) {
                  Thread.currentThread().interrupt();
                  return;
               }

               for (WatchEvent<?> event : key.pollEvents()) {
                  if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
                     eventListener.run();
                  }
               }
            } while (key.reset());
         });
      } catch (IOException e) {
         throw new IllegalStateException(e);
      }
   }

   private void registerAllDirectories(Path startDir) throws IOException {
      Files.walkFileTree(startDir, new SimpleFileVisitor<Path>() {
         public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            DirectoryWatcher.this.registerDirectory(dir);
            return FileVisitResult.CONTINUE;
         }
      });
   }

   private void registerDirectory(Path dir) throws IOException {
      dir.register(this.watchService, StandardWatchEventKinds.ENTRY_CREATE);
   }

   @Override
   public void close() {
      try {
         this.executorService.shutdownNow();
         this.executorService.awaitTermination(1L, TimeUnit.SECONDS);
         this.watchService.close();
      } catch (InterruptedException e) {
         Thread.currentThread().interrupt();
      } catch (IOException e) {
         throw new IllegalStateException(e);
      }
   }
}
