package ru.yandex.webmaster3.viewer.http.download;

import java.util.UUID;

import org.joda.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;

import ru.yandex.webmaster3.core.data.WebmasterHostId;
import ru.yandex.webmaster3.core.http.ActionStatus;
import ru.yandex.webmaster3.core.metrics.Category;
import ru.yandex.webmaster3.core.worker.client.WorkerClient;
import ru.yandex.webmaster3.core.worker.task.WorkerTaskData;
import ru.yandex.webmaster3.storage.util.ydb.exception.WebmasterYdbException;
import ru.yandex.webmaster3.storage.download.DownloadInfo;
import ru.yandex.webmaster3.storage.download.DownloadInfoYDao;
import ru.yandex.webmaster3.storage.download.DownloadStatus;
import ru.yandex.webmaster3.storage.host.CommonDataType;
import ru.yandex.webmaster3.storage.settings.SettingsService;
import ru.yandex.webmaster3.viewer.http.AbstractUrlFilteringAction;
import ru.yandex.webmaster3.viewer.http.host.verification.UsersVerifiedHostRequest;

/**
 * Created by ifilippov5 on 14.02.17.
 */
@Category("dowmload")
public abstract class DownloadAction<T extends UsersVerifiedHostRequest> extends AbstractUrlFilteringAction<T, DownloadResponse> {
    private static final Logger log = LoggerFactory.getLogger(DownloadAction.class);
    private WorkerClient workerClient;
    private DownloadInfoYDao downloadInfoYDao;
    private SettingsService settingsService;

    @Override
    public DownloadResponse process(T request) {
        long hash = getHash(request);
        DownloadInfo downloadInfo;
        try {
            downloadInfo = downloadInfoYDao.get(request.getHostId(), hash);
            if (downloadInfo == null || downloadInfo.getDownloadStatus() == DownloadStatus.INTERNAL_ERROR) {

                log.debug("Send download task with hostId = {} and hash = {} to worker", request.getHostId(), hash);
                downloadInfoYDao.add(request.getHostId(), hash, DownloadInfo.inProgress());

                boolean enqueued = workerClient.checkedEnqueueTask(getTaskData(request, hash));

                if (!enqueued) {
                    log.error("Download task with hostId = {} and hash = {} terminate in status Internal Error through error in worker", request.getHostId(), hash);
                    markFailed(request.getHostId(), hash);
                }
            } else {
                log.debug("Download task with hostId = {} and hash = {} was started early and has status {}", request.getHostId(), hash, downloadInfo.getDownloadStatus());
            }
            if (downloadInfo == null) {
                downloadInfo = downloadInfoYDao.get(request.getHostId(), hash);
            }
        } catch (Exception e) {
            markFailed(request.getHostId(), hash);
            log.error("Failed to process download request", e);
            throw e;
        }

        return new DownloadResponse(ActionStatus.SUCCESS, downloadInfo);
    }

    protected String generateFileName(T request) {
        return generateFileName(request, getHash(request));
    }

    protected String generateFileName(T request, long hash) {
        return request.getHostIdString() + "_" + Long.toHexString(getHash(request))
                + UUID.randomUUID().toString().substring(0, 8);
    }

    private void markFailed(WebmasterHostId hostId, long hash) {
        try {
            downloadInfoYDao.add(hostId, hash, DownloadInfo.error());
        } catch (WebmasterYdbException e) {
            log.error("Failed to mark task " + hash + " as failed", e);
        }
    }

    private int getMinutes() {
        return settingsService.getSettingCached(CommonDataType.DOWNLOAD_INTERVAL_BETWEEN_INTERNAL_ERRORS_IN_MINUTES, Integer::parseInt);
    }

    protected abstract long getHash(T request);

    @Deprecated
    protected abstract WorkerTaskData getTaskData(T request, long hash);

    @Required
    public void setWorkerClient(WorkerClient workerClient) {
        this.workerClient = workerClient;
    }

    @Required
    public void setDownloadInfoYDao(DownloadInfoYDao downloadInfoYDao) {
        this.downloadInfoYDao = downloadInfoYDao;
    }

    @Required
    public void setSettingsService(SettingsService settingsService) {
        this.settingsService = settingsService;
    }
}
