package ru.yandex.webmaster3.worker.searchbase;

import java.util.NavigableMap;
import java.util.UUID;

import com.datastax.driver.core.utils.UUIDs;
import lombok.Setter;
import org.joda.time.Duration;
import org.joda.time.Instant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.webmaster3.core.searchbase.SearchBaseUpdateInfo;
import ru.yandex.webmaster3.core.worker.task.PeriodicTaskState;
import ru.yandex.webmaster3.core.worker.task.PeriodicTaskType;
import ru.yandex.webmaster3.core.worker.task.TaskResult;
import ru.yandex.webmaster3.storage.util.ydb.exception.WebmasterYdbException;
import ru.yandex.webmaster3.storage.host.CommonDataState;
import ru.yandex.webmaster3.storage.host.CommonDataType;
import ru.yandex.webmaster3.storage.notifications.dao.SearchBaseNotificationsYDao;
import ru.yandex.webmaster3.storage.searchbase.dao.SearchBaseUpdatesYDao;
import ru.yandex.webmaster3.storage.settings.SettingsService;
import ru.yandex.webmaster3.worker.PeriodicTask;
import ru.yandex.webmaster3.worker.TaskSchedule;

/**
 * @author avhaliullin
 */
@Setter
public class StartSearchBaseUpdateNotificationTask extends PeriodicTask<PeriodicTaskState> {
    private static final Logger log = LoggerFactory.getLogger(StartSearchBaseUpdateNotificationTask.class);

    private static final Duration NOTIFICATION_TTL = Duration.standardDays(14);
    private static final int SEND_ATTEMPTS = 10;

    private SettingsService settingsService;
    private SearchBaseNotificationsYDao searchBaseNotificationsYDao;
    private SearchBaseUpdatesYDao searchBaseUpdatesYDao;

    @Override
    public Result run(UUID runId) throws Exception {

        CommonDataState value = settingsService.getSettingUncached(CommonDataType.LAST_SENT_SEARCH_UPDATE_NOTIFICATION);
        Instant lastSentBaseSwitchDate = value == null ? null : new Instant(Long.parseLong(value.getValue()));
        NavigableMap<Instant, SearchBaseUpdateInfo> updatesHistory = searchBaseUpdatesYDao.getUpdatesHistory();
        if (!updatesHistory.isEmpty()) {
            Instant lastBaseSwitchTime = updatesHistory.lastKey();
            if (lastSentBaseSwitchDate == null || lastBaseSwitchTime.isAfter(lastSentBaseSwitchDate)) {
                settingsService.update(CommonDataType.LAST_SENT_SEARCH_UPDATE_NOTIFICATION, String.valueOf(lastBaseSwitchTime.getMillis()));
                UUID notificationId = UUIDs.timeBased();
                log.info("Sending search base update notification {} for base switched {}", notificationId, lastBaseSwitchTime);
                long sleepTime = 100L;
                for (int i = SEND_ATTEMPTS - 1; i >= 0; i--) {
                    try {
                        searchBaseNotificationsYDao.insertNotification(
                                notificationId,
                                lastBaseSwitchTime.toDateTime(),
                                true,
                                true,
                                NOTIFICATION_TTL);
                        return new Result(TaskResult.SUCCESS);
                    } catch (WebmasterYdbException e) {
                        log.error("Failed to send search base update notification " + notificationId + ", " + i + " attempts left", e);
                        if (i <= 0) {
                            throw e;
                        }
                        Thread.sleep(sleepTime);
                        sleepTime *= 2;
                    }
                }
            }
        }
        return new Result(TaskResult.SUCCESS);
    }

    @Override
    public PeriodicTaskType getType() {
        return PeriodicTaskType.SEND_SEARCH_BASE_UPDATE_NOTIFICATION;
    }

    @Override
    public TaskSchedule getSchedule() {
        return TaskSchedule.startByCron("0 1/5 * * * *");
    }
}
