/*
 * Copyright 2017 Netflix, Inc.
 *
 * 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.netflix.spinnaker.front50.migrations;

import java.util.List;
import java.util.Map;
import com.netflix.spinnaker.front50.model.pipeline.Pipeline;
import com.netflix.spinnaker.front50.model.pipeline.PipelineDAO;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import rx.Observable;
import static org.slf4j.LoggerFactory.getLogger;

@Component
public class ExecutionEngineMigration implements Migration {

  private final PipelineDAO pipelineDAO;

  @Autowired
  public ExecutionEngineMigration(PipelineDAO pipelineDAO) {this.pipelineDAO = pipelineDAO;}

  @Override public boolean isValid() {
    return true;
  }

  @Override public void run() {
    log.info("Starting Linear -> Parallel Migration");
    Observable
      .from(pipelineDAO.all())
      .filter(this::isV1)
      .filter(this::hasDeployStage)
      .filter(this::hasNoCanaryStage)
      .filter(this::deploysOnlyToTest)
      .subscribe(this::migrate, this::onError);
  }

  private boolean isV1(Pipeline pipeline) {
    return !"v2".equals(pipeline.get("executionEngine"));
  }

  private boolean hasDeployStage(Pipeline pipeline) {
    return Observable
      .from((List<Map<String, Object>>) pipeline.get("stages"))
      .exists(stage -> "deploy".equals(stage.get("type")))
      .toBlocking()
      .first();
  }

  private boolean hasNoCanaryStage(Pipeline pipeline) {
    return !Observable
      .from((List<Map<String, Object>>) pipeline.get("stages"))
      .exists(stage -> "canary".equals(stage.get("type")))
      .toBlocking()
      .first();
  }

  private boolean deploysOnlyToTest(Pipeline pipeline) {
    return Observable
      .from((List<Map<String, Object>>) pipeline.get("stages"))
      .filter(stage -> "deploy".equals(stage.get("type")))
      .flatMapIterable(stage -> (List<Map<String, Object>>) stage.get("clusters"))
      .all(cluster -> "test".equals(cluster.get("account")))
      .toBlocking()
      .first();
  }

  private void migrate(Pipeline pipeline) {
    log.info("Migrating {} pipeline '{}' from v1 -> v2", pipeline.getApplication(), pipeline.getName());

    pipeline.put("executionEngine", "v2");
    pipelineDAO.update(pipeline.getId(), pipeline);

    log.info("Migrated {} pipeline '{}' from v1 -> v2", pipeline.getApplication(), pipeline.getName());
  }

  private void onError(Throwable throwable) {
    log.error("Error migrating pipeline to v2", throwable);
  }

  private static final Logger log = getLogger(ExecutionEngineMigration.class);
}

@@ -1485,16 +1485,18 @@
 his::has
+No
 DeploySt
@@ -1492,32 +1492,32 @@
 sNoDeployStage)%0A
-
       .filter(th
@@ -1542,47 +1542,8 @@
 ge)%0A
-      .filter(this::deploysOnlyToTest)%0A
     
@@ -1719,16 +1719,18 @@
 lean has
+No
 DeploySt
@@ -1757,32 +1757,33 @@
 e) %7B%0A    return 
+!
 Observable%0A     
@@ -2076,32 +2076,32 @@
 .get(%22stages%22))%0A
+
       .exists(st
@@ -2188,395 +2188,8 @@
  %7D%0A%0A
-  private boolean deploysOnlyToTest(Pipeline pipeline) %7B%0A    return Observable%0A      .from((List%3CMap%3CString, Object%3E%3E) pipeline.get(%22stages%22))%0A      .filter(stage -%3E %22deploy%22.equals(stage.get(%22type%22)))%0A      .flatMapIterable(stage -%3E (List%3CMap%3CString, Object%3E%3E) stage.get(%22clusters%22))%0A      .all(cluster -%3E %22test%22.equals(cluster.get(%22account%22)))%0A      .toBlocking()%0A      .first();%0A  %7D%0A%0A
   pr

migrate all non-deploy, non-canary pipelines