package in.oneton.idea.spring.assistant.plugin.initializr;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import lombok.Builder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;

import static com.intellij.openapi.util.io.FileUtil.copy;
import static com.intellij.openapi.util.io.FileUtil.createTempFile;
import static com.intellij.openapi.util.text.StringUtil.isEmpty;
import static com.intellij.openapi.util.text.StringUtil.isNotEmpty;
import static com.intellij.openapi.vfs.newvfs.RefreshQueue.getInstance;
import static com.intellij.util.io.HttpRequests.request;
import static com.intellij.util.io.ZipUtil.extract;
import static in.oneton.idea.spring.assistant.plugin.initializr.misc.InitializrUtil.markAsExecutable;
import static in.oneton.idea.spring.assistant.plugin.initializr.misc.InitializrUtil.userAgent;
import static java.util.Objects.requireNonNull;

class InitializerDownloader {
  private static final Logger log = Logger.getInstance(InitializerDownloader.class);

  private final InitializrModuleBuilder builder;

  InitializerDownloader(InitializrModuleBuilder builder) {
    this.builder = builder;
  }

  @NotNull
  private String extractFilenameFromContentDisposition(@Nullable String contentDispositionHeader) {
    String fileName = null;
    if (contentDispositionHeader != null) {
      fileName = contentDispositionHeader
          .replaceFirst(".*filename=\"?(?<fileName>[^;\"]+);?\"?.*", "${fileName}");
    }
    return !isEmpty(fileName) ? fileName : "unknown";
  }

  void execute(ProgressIndicator indicator) throws IOException {
    File tempFile = createTempFile("spring-assistant-template", ".tmp", true);
    String downloadUrl = builder.safeGetProjectCreationRequest().buildDownloadUrl();
    debug(() -> log.debug("Downloading project from: " + downloadUrl));
    Download download = request(downloadUrl).userAgent(userAgent()).connect(request -> {
      String contentType = request.getConnection().getContentType();
      boolean zip = isNotEmpty(contentType) && contentType.startsWith("application/zip");
      String contentDisposition = request.getConnection().getHeaderField("Content-Disposition");
      String fileName = extractFilenameFromContentDisposition(contentDisposition);
      indicator.setText(fileName);
      request.saveToFile(tempFile, indicator);
      return Download.builder().zip(zip).fileName(fileName).build();
    });
    indicator.setText("Please wait ...");
    File targetExtractionDir = new File(requireNonNull(builder.getContentEntryPath()));
    if (download.zip) {
      extract(tempFile, targetExtractionDir, null);
      markAsExecutable(targetExtractionDir, "gradlew");
      markAsExecutable(targetExtractionDir, "mvnw");
    } else {
      copy(tempFile, new File(targetExtractionDir, download.fileName));
    }

    VirtualFile targetFile =
        LocalFileSystem.getInstance().refreshAndFindFileByIoFile(targetExtractionDir);
    getInstance().refresh(false, true, null, targetFile);
  }

  /**
   * Debug logging can be enabled by adding fully classified class name/package name with # prefix
   * For eg., to enable debug logging, go `Help > Debug log settings` & type `#in.oneton.idea.spring.assistant
   *
   * @param doWhenDebug code to execute when debug is enabled
   */
  private void debug(Runnable doWhenDebug) {
    if (log.isDebugEnabled()) {
      doWhenDebug.run();
    }
  }


  @Builder
  private static class Download {
    private final boolean zip;
    private final String fileName;
  }

}