/**
 * Copyright (C) 2016 Red Hat, 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.redhat.ipaas.controllers;

import com.redhat.ipaas.core.EventBus;
import com.redhat.ipaas.core.Json;
import com.redhat.ipaas.dao.manager.DataManager;
import com.redhat.ipaas.model.ChangeEvent;
import com.redhat.ipaas.model.Kind;
import com.redhat.ipaas.model.integration.Integration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * This class tracks changes to Integrations and attempts to process them so that
 * their current status matches their desired status.
 */
@Service
public class IntegrationController {

    private final DataManager dataManager;
    private final EventBus eventBus;
    private final HashMap<Integration.Status, WorkflowHandler> handlers = new HashMap<>();
    private ExecutorService executor;
    private ScheduledExecutorService scheduler;
    private long retrySeconds = 60;

    @Autowired
    public IntegrationController(DataManager dataManager, EventBus eventBus, List<WorkflowHandler> handlers) {
        this.dataManager = dataManager;
        this.eventBus = eventBus;
        for (WorkflowHandler handler : handlers) {
            for (Integration.Status status : handler.getTriggerStatuses()) {
                this.handlers.put(status, handler);
            }
        }
    }

    @PostConstruct
    public void start() {
        executor = Executors.newSingleThreadExecutor();
        scheduler = Executors.newScheduledThreadPool(1);
        scanIntegrationsForWork();

        eventBus.subscribe("integration-controller", (event, data) -> {
            if ("change-event".equals(event)) {
                try {
                    ChangeEvent changeEvent = Json.mapper().readValue(data, ChangeEvent.class);
                    if (changeEvent != null || changeEvent.getKind().isPresent() && changeEvent.getId().isPresent()) {
                        if (Kind.from(changeEvent.getKind().get()) == Kind.Integration) {
                            enqueueCheckIntegration(changeEvent.getId().get());
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @PreDestroy
    public void stop() {
        eventBus.unsubscribe("integration-controller");
        scheduler.shutdownNow();
        executor.shutdownNow();
    }

    private void scanIntegrationsForWork() {
        dataManager.fetchAll(Integration.class).getItems().forEach(integration -> {
            checkIntegration(integration);
        });
    }

    private void enqueueCheckIntegration(String id) {
        executor.execute(() -> {
            checkIntegration(dataManager.fetch(Integration.class, id));
        });
    }

    private void checkIntegration(Integration integration) {
        Integration.Status desired = integration.getDesiredStatus().orElse(Integration.Status.Draft);
        Integration.Status current = integration.getCurrentStatus().orElse(Integration.Status.Draft);
        if (current != desired) {
            WorkflowHandler workflowHandler = handlers.get(desired);
            if (workflowHandler != null) {
                enqueue(workflowHandler, integration.getId().get());
            }
        }
    }

    private void enqueue(WorkflowHandler handler, String integrationId) {
        executor.submit(() -> {
            Integration integration = dataManager.fetch(Integration.class, integrationId);
            if (stale(handler, integration)) {
                return;
            }

            try {

                Integration.Status currentStatus = handler.execute(integration);
                if (currentStatus == null) {
                    return;
                }

                // handler.execute might block for while so refresh our copy of the integration
                // data before we update the current status
                integration = dataManager.fetch(Integration.class, integrationId);
                integration = new Integration.Builder().currentStatus(currentStatus).build();
                dataManager.update(integration);

            } catch (Exception e) {

                e.printStackTrace();
                // TODO update the integration status

                // Retry in a while
                scheduler.schedule(() -> {
                    enqueue(handler, integrationId);
                }, retrySeconds, TimeUnit.SECONDS);
            }
        });
    }

    private boolean stale(WorkflowHandler workflow, Integration integration) {
        if (integration == null) {
            return true;
        }
        if (!workflow.getTriggerStatuses().contains(integration.getDesiredStatus())) {
            return true;
        }
        return false;
    }

}

@@ -2513,16 +2513,31 @@
     if (
+event!=null && 
 %22change-
@@ -2556,24 +2556,24 @@
 s(event)) %7B%0A
-
             
@@ -2701,16 +2701,17 @@
      if 
+(
 (changeE
@@ -2722,16 +2722,40 @@
  != null
+%0A                       
  %7C%7C chan
@@ -2819,18 +2819,16 @@
 esent())
- %7B
 %0A       
@@ -2844,18 +2844,18 @@
         
-if
+&&
  (Kind.f
@@ -2903,31 +2903,28 @@
 Integration)
+)
  %7B%0A
-    
             
@@ -2991,34 +2991,8 @@
 ));%0A
-                        %7D%0A
     
@@ -3962,27 +3962,32 @@
 if (
+!
 current
- != 
+.equals(
 desired)
  %7B%0A 
@@ -3982,16 +3982,17 @@
 desired)
+)
  %7B%0A     
@@ -5489,20 +5489,35 @@
         
-if (
+return%0A            
 integrat
@@ -5519,35 +5519,32 @@
 egration == null
-) %7B
 %0A            ret
@@ -5544,39 +5544,40 @@
     
-return true;%0A    
+%7C%7C workflow == null%0A
     
-%7D%0A
         
 if (
@@ -5572,20 +5572,19 @@
         
-if (
+%7C%7C 
 !workflo
@@ -5650,67 +5650,8 @@
 s())
-) %7B%0A            return true;%0A        %7D%0A        return false
 ;%0A  

Apply codacy suggestions.