package com.njcn.gather.result.service;

import com.njcn.gather.result.pojo.param.DataCheckRequest;
import com.njcn.gather.result.pojo.vo.DataCheckNotifyContext;
import com.njcn.gather.result.service.impl.ResultServiceImpl;
import com.njcn.http.util.RestTemplateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Map;

@Slf4j
@Component
@RequiredArgsConstructor
public class DataCheckAsyncNotifier {
    @Value("${dataCheck.url}")
    private String url;
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private final RestTemplateUtil restTemplateUtil;

    @Async
    public void notifyAsync(DataCheckNotifyContext context, Map<String, ResultServiceImpl.NotifyState> notifyStates) {
        notifyDirect(context, notifyStates);
    }

    void notifyDirect(DataCheckNotifyContext context, Map<String, ResultServiceImpl.NotifyState> notifyStates) {
        int maxRetries = 3;
        for (int attempt = 1; attempt <= maxRetries; attempt++) {
            try {
                DataCheckRequest request = new DataCheckRequest();
                request.setLineIds(context.getLineIds());
                request.setIndicatorCodes(Collections.emptyList());
                request.setTimeStart(FORMATTER.format(context.getTimeStart()));
                request.setTimeEnd(FORMATTER.format(context.getTimeEnd()));

                restTemplateUtil.postJson(url, request, String.class);
                notifyStates.put(context.getNotifyKey(),
                        new ResultServiceImpl.NotifyState(ResultServiceImpl.NotifyStatus.SUCCESS, attempt - 1, null, LocalDateTime.now()));
                log.info("checksquare notify success, key={}, planId={}, lineCount={}", context.getNotifyKey(), context.getPlanId(), context.getLineIds().size());
                return;
            } catch (Exception ex) {
                int failCount = attempt;
                if (failCount > maxRetries) {
                    notifyStates.put(context.getNotifyKey(),
                            new ResultServiceImpl.NotifyState(ResultServiceImpl.NotifyStatus.FAIL, maxRetries, ex.getMessage(), LocalDateTime.now()));
                    log.error("checksquare notify failed, key={}, failCount={}", context.getNotifyKey(), maxRetries, ex);
                    return;
                }
                if (!sleepBeforeRetry(context, failCount)) {
                    notifyStates.put(context.getNotifyKey(),
                            new ResultServiceImpl.NotifyState(ResultServiceImpl.NotifyStatus.FAIL, failCount, "retry interrupted", LocalDateTime.now()));
                    return;
                }
            }
        }
    }

    private boolean sleepBeforeRetry(DataCheckNotifyContext context, int failCount) {
        long delayMillis = (long) Math.pow(2, failCount) * 1000L;
        log.warn("checksquare notify retry scheduled, key={}, failCount={}, delayMillis={}", context.getNotifyKey(), failCount, delayMillis);
        try {
            Thread.sleep(delayMillis);
            return true;
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            return false;
        }
    }
}
