package ru.kirillius.pf.sdn.service;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import ru.kirillius.pf.sdn.Util.WorkerUtils;
import ru.kirillius.pf.sdn.api.Networking.DomainResolver;

import java.net.Inet4Address;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;

@Service
@RequiredArgsConstructor
@Slf4j
public class DomainUpdaterService {
    private final DomainCacheService domainCacheService;
    private final DomainResolver domainResolver;
    private final ExecutorService executor;
    private final SubscriptionService subscriptionService;

    public List<Inet4Address> getActualAddresses(String domain) {
        var cached = domainCacheService.load(domain, true);
        if (!cached.isEmpty()) {
            var resolved = domainResolver.getAddresses(domain);
            while (!resolved.isDone() && !resolved.isCancelled()) {
                Thread.yield();
            }
            if (resolved.isDone()) {
                try {
                    domainCacheService.update(domain, resolved.get());
                    cached.addAll(resolved.get());
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return cached;
    }

    private Future<?> worker;
    private final RuntimeConfigService configService;

    @PostConstruct
    private void initialize() {
        worker = executor.submit(() -> new WorkerUtils.LoopedWorker() {
            @Override
            protected void doWork() {
                subscriptionService.getSubscriptions().forEach(subscription -> {
                    var scope = subscription.getScope();
                    if (scope.getResolveDomains().isEmpty()) {
                        return;
                    }
                    scope.getDomains().forEach(domain -> {
                        var future = domainResolver.getAddresses(domain);
                        while (!future.isDone() && !future.isCancelled()) {
                            Thread.yield();
                        }

                        if (future.isDone()) {
                            try {
                                domainCacheService.update(domain, future.get());
                            } catch (InterruptedException | ExecutionException e) {
                                log.error("Domain {} resolve failed", domain);
                            }
                        }
                    });
                });

                WorkerUtils.waitForTimer(() -> Duration.ofMinutes(configService.getConfig().getDomainResolveInterval()));

            }
        });

    }

    @PreDestroy
    private void destroy() {
        if (worker != null) {
            worker.cancel(true);
            worker = null;
        }
    }
}
