// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.io;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("UnitTests")
class ZipTest {
  private File inputDir;
  private File outputDir;
  private TemporaryFilesystem tmpFs;

  @BeforeEach
  public void setUp() {
    File baseForTest = new File(System.getProperty("java.io.tmpdir"), "tmpTest");
    baseForTest.mkdir();
    tmpFs = TemporaryFilesystem.getTmpFsBasedOn(baseForTest);

    inputDir = tmpFs.createTempDir("input", "ziptest");
    outputDir = tmpFs.createTempDir("output", "ziptest");
  }

  @AfterEach
  public void tearDown() {
    tmpFs.deleteTemporaryFiles();
  }

  @Test
  void testCanZipASingleFile() throws IOException {
    File input = new File(inputDir, "foo.txt");
    File unwanted = new File(inputDir, "nay.txt");
    writeTestFile(input);
    writeTestFile(unwanted);

    String zipped = Zip.zip(input);

    Zip.unzip(zipped, outputDir);
    File unzipped = new File(outputDir, "foo.txt");
    File notThere = new File(outputDir, "nay.txt");

    assertThat(unzipped).exists();
    assertThat(notThere).doesNotExist();
  }

  @Test
  void testCanZipADirectory() throws IOException {
    File input1 = new File(inputDir, "foo.txt");
    File input2 = new File(inputDir, "bar/bar.txt");
    writeTestFile(input1);
    writeTestFile(input2);

    String zipped = Zip.zip(inputDir);

    Zip.unzip(zipped, outputDir);
    File unzipped1 = new File(outputDir, "foo.txt");
    File unzipped2 = new File(outputDir, "bar/bar.txt");

    assertThat(unzipped1).exists();
    assertThat(unzipped2).exists();
  }

  @Test
  void testCanUnzip() throws IOException {
    File testZip = File.createTempFile("testUnzip", "zip");
    writeTestZip(testZip, 25);
    File out = Zip.unzipToTempDir(new FileInputStream(testZip), "unzip", "stream");
    assertThat(out.list()).hasSize(25);
  }

  private File writeTestZip(File file, int files) throws IOException {
    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
    for (int i = 0; i < files; i++) {
      writeTestZipEntry(out);
    }
    out.close();
    file.deleteOnExit();
    return file;
  }

  private void writeTestZipEntry(ZipOutputStream out) throws IOException {
    File testFile = File.createTempFile("testZip", "file");
    writeTestFile(testFile);
    ZipEntry entry = new ZipEntry(testFile.getName());
    out.putNextEntry(entry);
    try (FileInputStream in = new FileInputStream(testFile)) {
      byte[] buffer = new byte[16384];
      while (in.read(buffer, 0, 16384) != -1) {
        out.write(buffer);
      }
    }
    out.flush();
  }

  private void writeTestFile(File file) throws IOException {
    File parent = file.getParentFile();
    if (!parent.exists()) {
      assertThat(parent.mkdirs()).isTrue();
    }
    byte[] byteArray = new byte[16384];
    new Random().nextBytes(byteArray);
    try (OutputStream out = new FileOutputStream(file)) {
      out.write(byteArray);
    }
    file.deleteOnExit();
  }
}
