package com.writeoff.module.scheduler.repository;

import com.writeoff.module.scheduler.model.AsyncJob;
import com.writeoff.module.scheduler.model.AsyncJobStatus;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

@Repository
@ConditionalOnProperty(prefix = "app.repository", name = "mode", havingValue = "in-memory")
public class InMemoryAsyncJobRepository implements AsyncJobRepository {
    private final ConcurrentHashMap<Long, AsyncJob> store = new ConcurrentHashMap<>();
    private final AtomicLong idGenerator = new AtomicLong(10000);

    @Override
    public AsyncJob save(AsyncJob job) {
        if (job.getId() == null) {
            AsyncJob newJob = new AsyncJob(
                    idGenerator.incrementAndGet(),
                    job.getTenantId(),
                    job.getJobType(),
                    job.getPayload(),
                    job.getStatus(),
                    job.getNextRunAt(),
                    job.getRetryCount(),
                    job.getMaxRetry(),
                    job.getIdempotencyKey(),
                    job.getLockedBy(),
                    job.getLockedAt()
            );
            store.put(newJob.getId(), newJob);
            return newJob;
        }
        store.put(job.getId(), job);
        return job;
    }

    @Override
    public Optional<AsyncJob> findById(Long id) {
        return Optional.ofNullable(store.get(id));
    }

    @Override
    public Optional<AsyncJob> findByIdempotencyKey(String idempotencyKey) {
        if (idempotencyKey == null || idempotencyKey.isEmpty()) {
            return Optional.empty();
        }
        return store.values().stream()
                .filter(j -> idempotencyKey.equals(j.getIdempotencyKey()))
                .findFirst();
    }

    @Override
    public List<AsyncJob> findReady(LocalDateTime now, int limit) {
        return store.values().stream()
                .filter(j -> j.getStatus() == AsyncJobStatus.READY && !j.getNextRunAt().isAfter(now))
                .sorted(Comparator.comparing(AsyncJob::getNextRunAt))
                .limit(limit)
                .collect(Collectors.toCollection(ArrayList::new));
    }
}
