/*
 * Copyright 2020 Google LLC.
 *
 * Licensed 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 com.google.cloud.tools.jib.cli;

import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;

import com.google.cloud.tools.jib.api.Credential;
import com.google.cloud.tools.jib.plugins.common.DefaultCredentialRetrievers;
import java.io.FileNotFoundException;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import picocli.CommandLine;

@RunWith(JUnitParamsRunner.class)
public class CredentialsTest {

  private static final String[] DEFAULT_ARGS = {"--target=ignored"};
  @Rule public final MockitoRule mockitoJUnit = MockitoJUnit.rule();
  @Mock private DefaultCredentialRetrievers defaultCredentialRetrievers;

  private String[][] paramsToNone() {
    return new String[][] {
      {"--from-credential-helper=ignored"}, {"--from-username=ignored", "--from-password=ignored"},
    };
  }

  @Test
  @Parameters(method = "paramsToNone")
  public void testGetToCredentialRetriever_none(String[] args) throws FileNotFoundException {
    CommonCliOptions commonCliOptions =
        CommandLine.populateCommand(new CommonCliOptions(), ArrayUtils.addAll(DEFAULT_ARGS, args));
    Credentials.getToCredentialRetrievers(commonCliOptions, defaultCredentialRetrievers);
    verify(defaultCredentialRetrievers).asList();
    verifyNoMoreInteractions(defaultCredentialRetrievers);
  }

  private String[][] paramsFromNone() {
    return new String[][] {
      {"--to-credential-helper=ignored"}, {"--to-username=ignored", "--to-password=ignored"},
    };
  }

  @Test
  @Parameters(method = "paramsFromNone")
  public void testGetFromCredentialRetriever_none(String[] args) throws FileNotFoundException {
    CommonCliOptions commonCliOptions =
        CommandLine.populateCommand(new CommonCliOptions(), ArrayUtils.addAll(DEFAULT_ARGS, args));
    Credentials.getFromCredentialRetrievers(commonCliOptions, defaultCredentialRetrievers);
    verify(defaultCredentialRetrievers).asList();
    verifyNoMoreInteractions(defaultCredentialRetrievers);
  }

  private String[][] paramsToCredHelper() {
    return new String[][] {
      {"--credential-helper=abc"},
      {"--to-credential-helper=abc"},
      {"--to-credential-helper=abc", "--from-credential-helper=ignored"},
      {"--to-credential-helper=abc", "--from-username=ignored", "--from-password=ignored"},
    };
  }

  @Test
  @Parameters(method = "paramsToCredHelper")
  public void testGetToCredentialRetriever_credHelper(String[] args) throws FileNotFoundException {
    CommonCliOptions commonCliOptions =
        CommandLine.populateCommand(new CommonCliOptions(), ArrayUtils.addAll(DEFAULT_ARGS, args));
    Credentials.getToCredentialRetrievers(commonCliOptions, defaultCredentialRetrievers);
    verify(defaultCredentialRetrievers).setCredentialHelper("abc");
    verify(defaultCredentialRetrievers).asList();
    verifyNoMoreInteractions(defaultCredentialRetrievers);
  }

  private String[][] paramsFromCredHelper() {
    return new String[][] {
      {"--credential-helper=abc"},
      {"--from-credential-helper=abc"},
      {"--from-credential-helper=abc", "--to-credential-helper=ignored"},
      {"--from-credential-helper=abc", "--to-username=ignored", "--to-password=ignored"},
    };
  }

  @Test
  @Parameters(method = "paramsFromCredHelper")
  public void testGetFromCredentialHelper(String[] args) throws FileNotFoundException {
    CommonCliOptions commonCliOptions =
        CommandLine.populateCommand(new CommonCliOptions(), ArrayUtils.addAll(DEFAULT_ARGS, args));
    Credentials.getFromCredentialRetrievers(commonCliOptions, defaultCredentialRetrievers);
    verify(defaultCredentialRetrievers).setCredentialHelper("abc");
    verify(defaultCredentialRetrievers).asList();
    verifyNoMoreInteractions(defaultCredentialRetrievers);
  }

  public Object paramsToUsernamePassword() {
    return new Object[][] {
      {"--username/--password", new String[] {"--username=abc", "--password=xyz"}},
      {"--to-username/--to-password", new String[] {"--to-username=abc", "--to-password=xyz"}},
      {
        "--to-username/--to-password",
        new String[] {
          "--to-username=abc",
          "--to-password=xyz",
          "--from-username=ignored",
          "--from-password=ignored"
        }
      },
      {
        "--to-username/--to-password",
        new String[] {"--to-username=abc", "--to-password=xyz", "--from-credential-helper=ignored"}
      }
    };
  }

  @Test
  @Parameters(method = "paramsToUsernamePassword")
  public void testGetToUsernamePassword(String expectedSource, String[] args)
      throws FileNotFoundException {
    CommonCliOptions commonCliOptions =
        CommandLine.populateCommand(new CommonCliOptions(), ArrayUtils.addAll(DEFAULT_ARGS, args));
    Credentials.getToCredentialRetrievers(commonCliOptions, defaultCredentialRetrievers);
    ArgumentCaptor<Credential> captor = ArgumentCaptor.forClass(Credential.class);
    verify(defaultCredentialRetrievers)
        .setKnownCredential(captor.capture(), ArgumentMatchers.eq(expectedSource));
    assertThat(captor.getValue()).isEqualTo(Credential.from("abc", "xyz"));
    verify(defaultCredentialRetrievers).asList();
    verifyNoMoreInteractions(defaultCredentialRetrievers);
  }

  public Object paramsFromUsernamePassword() {
    return new Object[][] {
      {"--username/--password", new String[] {"--username=abc", "--password=xyz"}},
      {
        "--from-username/--from-password",
        new String[] {"--from-username=abc", "--from-password=xyz"}
      },
      {
        "--from-username/--from-password",
        new String[] {
          "--from-username=abc",
          "--from-password=xyz",
          "--to-username=ignored",
          "--to-password=ignored"
        }
      },
      {
        "--from-username/--from-password",
        new String[] {
          "--from-username=abc", "--from-password=xyz", "--to-credential-helper=ignored"
        }
      },
    };
  }

  @Test
  @Parameters(method = "paramsFromUsernamePassword")
  public void testGetFromUsernamePassword(String expectedSource, String[] args)
      throws FileNotFoundException {
    CommonCliOptions commonCliOptions =
        CommandLine.populateCommand(new CommonCliOptions(), ArrayUtils.addAll(DEFAULT_ARGS, args));
    Credentials.getFromCredentialRetrievers(commonCliOptions, defaultCredentialRetrievers);
    ArgumentCaptor<Credential> captor = ArgumentCaptor.forClass(Credential.class);
    verify(defaultCredentialRetrievers)
        .setKnownCredential(captor.capture(), ArgumentMatchers.eq(expectedSource));
    assertThat(captor.getValue()).isEqualTo(Credential.from("abc", "xyz"));
    verify(defaultCredentialRetrievers).asList();
    verifyNoMoreInteractions(defaultCredentialRetrievers);
  }
}
