package ru.nbch.credit_tracker.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.dto.PhoneFidMap;
import ru.nbch.credit_tracker.entities.dto.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.service.signal.SignalRestService;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

/**
 * Сервис периодического обновления фидов и флагов просрочек по пакетам находящимся в статусе ON_MONITORING
 * По обновленным пакетам формируется новый запрос постановки на мониторинг в официальный сервис сигналов
 */
@Slf4j
@Service
public class UpdatingFlagsService {

    private final SignalService signalService;
    private final SignalRestService signalRestService;
    private final MonitoringErrorProcesingService monitoringErrorProcesingService;
    private final Long daysToUpdateFlags;
    private final Long updateFlagsInterval;

    public UpdatingFlagsService(SignalService signalService,
                                SignalRestService signalRestService,
                                MonitoringErrorProcesingService monitoringErrorProcesingService,
                                CreditTrackerSettings config) {
        this.signalService = signalService;
        this.signalRestService = signalRestService;
        this.monitoringErrorProcesingService = monitoringErrorProcesingService;
        this.daysToUpdateFlags = config.getDaysToUpdateFlags() != null ? config.getDaysToUpdateFlags() : 1;
        this.updateFlagsInterval = config.getUpdateFlagsInterval() != null ? config.getUpdateFlagsInterval() : 1;
    }

    public void run() {
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new CustomizableThreadFactory("updating-flags-"));
        scheduler.scheduleAtFixedRate(this::update, 0, updateFlagsInterval, TimeUnit.DAYS);
    }

    private void update() {
        log.info("Start updating flags");
        List<UserPackage> packages = signalService.findPackagesOnMonitoring();
        for (UserPackage userPackage : packages) {
            log.info("Updating flags for Package {}", userPackage.getPackageName());
            updateFlags(userPackage.getId(), userPackage.getPackageName());
        }
        log.info("Finish updating flags");
    }

    private void updateFlags(Integer packageId, String packageName) {
        try {
            signalService.updatePackageStatus(packageId, Status.PROCESSING);
            Predicate<PhoneFidMap> filter = subject -> subject.getCalculatedAt() != null && hasDaysPassed(subject.getCalculatedAt());
            SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, filter);
            if (updateResult.isUpdated()) {
                if (signalRestService.setupMonitoring(packageId)) {
                    log.info("Flags were updated successfully. PackageId: {} PackageName: {}. Package status changed to ON_MONITORING", packageId, packageName);
                    signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
                    monitoringErrorProcesingService.findErrors(packageId);
                } else {
                    log.info("Updated package registration was rejected by signal service. PackageId: {} PackageName: {}", packageId, packageName);
                    signalService.updatePackageStatus(packageId, Status.ERROR);
                }
            } else {
                log.info("No flags were updated for package id {}. Package status changed to ON_MONITORING", packageId);
                signalService.updatePackageStatus(packageId, Status.ON_MONITORING);
            }
        } catch (Exception e) {
            log.error("Ошибка при поиске фидов или перерасчете флагов просрочек. Package_id {}", packageId, e);
        }
    }

    private boolean hasDaysPassed(LocalDateTime pastDateTime) {
        LocalDateTime now = LocalDateTime.now();
        long daysPassed = ChronoUnit.DAYS.between(pastDateTime, now);
        return daysPassed >= daysToUpdateFlags;
    }
}
