package org.ovirt.engine.core.bll.storage;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.ovirt.engine.core.bll.Backend;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.action.SetStoragePoolStatusParameters;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.businessentities.StoragePoolStatus;
import org.ovirt.engine.core.common.businessentities.StoragePool;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.utils.timer.OnTimerMethodAnnotation;
import org.ovirt.engine.core.utils.timer.SchedulerUtil;
import org.ovirt.engine.core.utils.timer.SchedulerUtilQuartzImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;

public final class StoragePoolStatusHandler {
    private static final Logger log = LoggerFactory.getLogger(StoragePoolStatusHandler.class);

    private static HashMap<Guid, StoragePoolStatusHandler> _nonOperationalPools = new HashMap<>();

    private Guid poolId;
    private String timerId;
    @Inject
    private SchedulerUtilQuartzImpl schedulerUtil;

    private StoragePoolStatusHandler(Guid poolId) {
        this.poolId = poolId;
        timerId = null;
    }

    protected SchedulerUtil getScheduler() {
        return schedulerUtil;
    }

    private StoragePoolStatusHandler scheduleTimeout() {
        Class[] argTypes = new Class[0];
        Object[] args = new Object[0];
        Integer timeout = Config.<Integer> getValue(ConfigValues.StoragePoolNonOperationalResetTimeoutInMin);

        timerId = getScheduler().scheduleAOneTimeJob(this, "onTimeout", argTypes, args, timeout, TimeUnit.MINUTES);

        return this;
    }

    private void deScheduleTimeout() {
        if (timerId != null) {
            getScheduler().deleteJob(timerId);
            timerId = null;
        }
    }

    @OnTimerMethodAnnotation("onTimeout")
    public void onTimeout() {
        if (_nonOperationalPools.containsKey(poolId)) {
            try {
                StoragePool pool = DbFacade.getInstance().getStoragePoolDao().get(poolId);
                if (pool != null && pool.getStatus() == StoragePoolStatus.NotOperational) {
                    nonOperationalPoolTreatment(pool);
                }
            } catch (Exception e) {
            }
        }
    }

    public static void poolStatusChanged(Guid poolId, StoragePoolStatus status) {
        if (_nonOperationalPools.containsKey(poolId) && status != StoragePoolStatus.NotOperational) {
            StoragePoolStatusHandler handler = _nonOperationalPools.get(poolId);

            if (handler != null) {
                synchronized (handler) {
                    handler.deScheduleTimeout();
                }
            }
            synchronized (_nonOperationalPools) {
                _nonOperationalPools.remove(poolId);
            }
        } else if (status == StoragePoolStatus.NotOperational) {
            synchronized (_nonOperationalPools) {
                _nonOperationalPools.put(poolId, new StoragePoolStatusHandler(poolId).scheduleTimeout());
            }
        }
    }

    private static void nonOperationalPoolTreatment(StoragePool pool) {
        boolean changeStatus = false;
        if (StorageHandlingCommandBase.getAllRunningVdssInPool(pool).size() > 0) {
            changeStatus = true;
        }
        if (changeStatus) {
            log.info("Moving data center '{}' with Id '{}' to status Problematic from status NotOperational on a one"
                    + " time basis to try to recover",
                    pool.getName(),
                    pool.getId());
            Backend.getInstance().runInternalAction(
                    VdcActionType.SetStoragePoolStatus,
                    new SetStoragePoolStatusParameters(pool.getId(), StoragePoolStatus.NonResponsive,
                            AuditLogType.SYSTEM_CHANGE_STORAGE_POOL_STATUS_PROBLEMATIC_FROM_NON_OPERATIONAL));
            synchronized (_nonOperationalPools) {
                _nonOperationalPools.remove(pool.getId());
            }
        }
    }

    public static void init() {
        List<StoragePool> allPools = DbFacade.getInstance().getStoragePoolDao().getAll();
        for (StoragePool pool : allPools) {
            if (pool.getStatus() == StoragePoolStatus.NotOperational) {
                poolStatusChanged(pool.getId(), StoragePoolStatus.NotOperational);
            }
        }
    }
}

@@ -86,16 +86,42 @@
 l.List;%0A
+import java.util.Objects;%0A
 import j
@@ -440,22 +440,16 @@
 ragePool
-Status
 ;%0Aimport
@@ -498,32 +498,38 @@
 ties.StoragePool
+Status
 ;%0Aimport org.ovi
@@ -716,16 +716,58 @@
 Facade;%0A
+import org.ovirt.engine.core.di.Injector;%0A
 import o
@@ -1008,37 +1008,8 @@
 y;%0A%0A
-import javax.inject.Inject;%0A%0A
 publ
@@ -1258,16 +1258,22 @@
 private 
+final 
 Guid poo
@@ -1293,47 +1293,13 @@
 ate 
-String timerId;%0A    @Inject%0A    private
+final
  Sch
@@ -1335,16 +1335,45 @@
 rUtil;%0A%0A
+    private String timerId;%0A%0A
     priv
@@ -1416,50 +1416,218 @@
 olId
-) %7B%0A        this.poolId = poolId;%0A        
+, SchedulerUtilQuartzImpl schedulerUtil) %7B%0A        Objects.requireNonNull(schedulerUtil, %22schedulerUtil cannot be null%22);%0A%0A        this.poolId = poolId;%0A        this.schedulerUtil = schedulerUtil;%0A        this.
 time
@@ -3426,77 +3426,348 @@
     
-_nonOperationalPools.put(poolId, new StoragePoolStatusHandler(poolId)
+final SchedulerUtilQuartzImpl schedulerUtil = Injector.get(SchedulerUtilQuartzImpl.class);%0A                final StoragePoolStatusHandler storagePoolStatusHandler = new StoragePoolStatusHandler(%0A                        poolId,%0A                        schedulerUtil);%0A                _nonOperationalPools.put(poolId, storagePoolStatusHandler
 .sch

fix StoragePoolStatusHandler initialization