/*-
 * -\-\-
 * Helios Testing Library
 * --
 * Copyright (C) 2016 Spotify AB
 * --
 * 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.spotify.helios.testing;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static com.spotify.helios.testing.TemporaryJobsTestBase.temporaryJobsBuilder;
import static java.lang.String.format;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.junit.experimental.results.PrintableResult.testResult;
import static org.junit.experimental.results.ResultMatchers.isSuccessful;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ListenableFuture;
import com.spotify.helios.client.HeliosClient;
import com.spotify.helios.common.descriptors.Job;
import com.spotify.helios.common.descriptors.JobId;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class ConfigTest {

  private static TestParameters parameters;

  @Mock
  private static HeliosClient client;

  @Mock(answer = Answers.CALLS_REAL_METHODS)
  private static HeliosClient.Builder clientBuilder;

  @Before
  public void setup() {
    final ListenableFuture<Map<JobId, Job>> future =
        immediateFuture((Map<JobId, Job>) new HashMap<JobId, Job>());

    // Return an empty job list to skip trying to remove old jobs
    when(client.jobs()).thenReturn(future);
    when(client.jobs(anyString())).thenReturn(future);
  }

  public static class ProfileTest implements Deployer {

    // Local is the default profile, so don't specify it explicitly to test default loading
    @Rule
    public final TemporaryJobs temporaryJobs = parameters.builder
        .client(client)
        .deployer(this)
        .build();

    @Before
    public void setup() {
      // This job will get deployed test-host
      temporaryJobs.job().deploy("test-host");

      // this job will get deployed using the host filter in the conf file
      temporaryJobs.job().deploy();
    }

    @Test
    public void test() throws Exception {
      // Dummy test so junit doesn't complain.
    }

    @Override
    public TemporaryJob deploy(Job job, List<String> hosts, Set<String> waitPorts, Prober prober,
                               TemporaryJobReports.ReportWriter reportWriter) {
      // This is called when the first job is deployed
      assertThat(hosts, equalTo((List<String>) newArrayList("test-host")));
      parameters.validate(job, temporaryJobs.prefix());
      return null;
    }

    @Override
    public TemporaryJob deploy(Job job, String hostFilter, Set<String> waitPorts, Prober prober,
                               TemporaryJobReports.ReportWriter reportWriter) {
      // This is called when the second job is deployed
      assertThat(hostFilter, equalTo(parameters.hostFilter));
      parameters.validate(job, temporaryJobs.prefix());
      return null;
    }

    @Override
    public void readyToDeploy() {
    }
  }

  @Test
  public void testLocalProfile() throws Exception {
    final TestParameters.JobValidator validator = new TestParameters.JobValidator() {
      @Override
      public void validate(Job job, String prefix) {
        final String local = prefix + ".local.";
        final Map<String, String> map = ImmutableMap.of(
            "SPOTIFY_TEST_THING", format("See, we used the prefix here -->%s<--", prefix),
            "SPOTIFY_DOMAIN", local,
            "SPOTIFY_POD", local);

        assertThat(job.getEnv(), equalTo(map));
        assertThat(job.getImage(), equalTo("spotify/busybox:latest"));
      }
    };

    // The local profile is the default, so we don't specify it explicitly so we can test
    // the default loading mechanism.
    parameters = new TestParameters(temporaryJobsBuilder(),
        ".*", validator);
    assertThat(testResult(ProfileTest.class), isSuccessful());
  }

  @Test
  public void testHeliosCiProfile() throws Exception {
    final TestParameters.JobValidator validator = new TestParameters.JobValidator() {
      @Override
      public void validate(Job job, String prefix) {
        final String domain = prefix + ".services.helios-ci.cloud.spotify.net";
        final Map<String, String> map = ImmutableMap.of(
            "SPOTIFY_DOMAIN", domain,
            "SPOTIFY_POD", domain,
            "SPOTIFY_SYSLOG_HOST", "10.99.0.1");
        assertThat(job.getEnv(), equalTo(map));
        assertThat(job.getImage(), equalTo("spotify/busybox:latest"));

      }
    };

    // Specify the helios-ci profile explicitly, but make sure that the construction of the
    // HeliosClient used by TemporaryJobs is mocked out to avoid attempting to connect to
    // possibly-unresolvable hosts.
    doReturn(client).when(clientBuilder).build();
    final TemporaryJobs.Builder builder =
        TemporaryJobs.builder("helios-ci", Collections.<String, String>emptyMap(), clientBuilder);
    parameters = new TestParameters(builder, ".+\\.helios-ci\\.cloud", validator);
    assertThat(testResult(ProfileTest.class), isSuccessful());
  }

  /**
   * Helper class allows us to inject different test parameters into ProfileTest so we can
   * reuse it to test multiple profiles.
   */
  private static class TestParameters {
    private final TemporaryJobs.Builder builder;
    private final String hostFilter;
    private final JobValidator jobValidator;

    private TestParameters(TemporaryJobs.Builder builder, String hostFilter,
                           JobValidator jobValidator) {
      this.builder = builder;
      this.hostFilter = hostFilter;
      this.jobValidator = jobValidator;
    }

    private void validate(final Job job, final String prefix) {
      jobValidator.validate(job, prefix);
    }

    private interface JobValidator {
      void validate(Job job, String prefix);
    }
  }
}
