package com.writeoff.module.scheduler.service;

import com.writeoff.common.exception.BusinessException;
import com.writeoff.module.observability.service.ObservabilityService;
import com.writeoff.module.scheduler.model.AsyncJob;
import com.writeoff.module.scheduler.model.AsyncJobStatus;
import com.writeoff.module.scheduler.repository.AsyncJobRepository;
import com.writeoff.security.AuthContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class AsyncJobService {
    private static final Logger log = LoggerFactory.getLogger(AsyncJobService.class);
    private final AsyncJobRepository asyncJobRepository;
    private final ObservabilityService observabilityService;

    @Autowired
    public AsyncJobService(AsyncJobRepository asyncJobRepository, ObservabilityService observabilityService) {
        this.asyncJobRepository = asyncJobRepository;
        this.observabilityService = observabilityService;
    }

    public AsyncJobService(AsyncJobRepository asyncJobRepository) {
        this(asyncJobRepository, null);
    }

    public AsyncJob enqueue(String jobType, String payload, String idempotencyKey) {
        if (idempotencyKey != null && asyncJobRepository.findByIdempotencyKey(idempotencyKey).isPresent()) {
            throw new BusinessException(10002, "请求幂等冲突");
        }
        AsyncJob job = new AsyncJob(
                null,
                AuthContext.tenantId(),
                jobType,
                payload,
                AsyncJobStatus.READY,
                LocalDateTime.now(),
                0,
                3,
                idempotencyKey,
                null,
                null
        );
        AsyncJob saved = asyncJobRepository.save(job);
        if (observabilityService != null) {
            observabilityService.recordAsyncMetric(jobType, "READY");
        }
        return saved;
    }

    public List<AsyncJob> fetchReadyJobs(int limit) {
        return asyncJobRepository.findReady(LocalDateTime.now(), limit);
    }

    public void markRunning(AsyncJob job, String worker) {
        job.setStatus(AsyncJobStatus.RUNNING);
        job.setLockedBy(worker);
        job.setLockedAt(LocalDateTime.now());
        asyncJobRepository.save(job);
    }

    public void markSuccess(AsyncJob job) {
        job.setStatus(AsyncJobStatus.SUCCESS);
        asyncJobRepository.save(job);
        if (observabilityService != null) {
            observabilityService.recordAsyncMetric(job.getJobType(), "SUCCESS");
        }
    }

    public void markFailed(AsyncJob job, Exception ex) {
        int retry = job.getRetryCount() + 1;
        job.setRetryCount(retry);
        if (retry >= job.getMaxRetry()) {
            job.setStatus(AsyncJobStatus.FAILED);
            log.error("Async job failed permanently, jobId={}, type={}, err={}", job.getId(), job.getJobType(), ex.getMessage());
        } else {
            job.setStatus(AsyncJobStatus.READY);
            job.setNextRunAt(LocalDateTime.now().plusSeconds(retry == 1 ? 30 : (retry == 2 ? 120 : 300)));
            log.warn("Async job retry scheduled, jobId={}, retry={}", job.getId(), retry);
        }
        asyncJobRepository.save(job);
        if (observabilityService != null) {
            observabilityService.recordAsyncMetric(job.getJobType(), "FAILED");
        }
    }
}
