package com.quyunshuo.module.home.utils;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import io.reactivex.Observable;
import io.reactivex.Scheduler;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * 使用方式参考RxHttpUploader
 * 基于 RxJava 的上传工具基类
 * @param <T> 上传数据类型
 *周期1:
 *   尝试1 → 失败 → 立即重试 (延迟 = 2000 * 2^0 = 2秒)
 *   尝试2 → 失败 → 等待4秒 (2000 * 2^1 = 4秒)
 *   尝试3 → 失败 → 触发重置 → 等待30秒
 * 周期2:
 *   尝试4 → 失败 → 立即重试 (延迟 = 2000 * 2^0 = 2秒)
 *   尝试5 → 失败 → 等待4秒 (2000 * 2^1 = 4秒)
 *   尝试6 → 失败 → 触发重置 → 等待30秒
 * 周期3:
 *   尝试7 → 失败 → 立即重试 (延迟 = 2秒)
 *   尝试8 → 失败 → 等待4秒
 *   尝试9 → 成功 → 回调onSuccess()
 */
public abstract class RxUploader<T> {
    //基础重试间隔
    private final long baseRetryInterval;
    // 指数退避因子
    private final double backoffFactor;
    private final UploadCallback callback;
    private final Scheduler workScheduler;

    //重置间隔（毫秒）
    private final long resetInterval;
    //重试多少次后重置
    private final int resetAfterAttempts;

    private Disposable disposable;
    private final AtomicInteger totalAttempts = new AtomicInteger(0);
    private final AtomicInteger cycleAttempts = new AtomicInteger(0); // 当前周期内的尝试次数

    public interface UploadCallback {
        void onSuccess();
        void onFailure(String error);
        void onRetry(int attempt, long nextRetryDelay);
        void onReset(long resetDelay); // 新增回调
    }

    /**
     * 构造函数（无限重试版本）
     */
    public RxUploader(long baseRetryInterval,
                      double backoffFactor,
                      long resetInterval,
                      int resetAfterAttempts,
                      UploadCallback callback) {
        this(baseRetryInterval, backoffFactor,
                resetInterval, resetAfterAttempts,
                Schedulers.io(), callback);
    }

    public RxUploader(long baseRetryInterval,
                      double backoffFactor,
                      long resetInterval,
                      int resetAfterAttempts,
                      Scheduler workScheduler,
                      UploadCallback callback) {
        this.baseRetryInterval = baseRetryInterval;
        this.backoffFactor = backoffFactor;
        this.resetInterval = resetInterval;
        this.resetAfterAttempts = resetAfterAttempts;
        this.workScheduler = workScheduler;
        this.callback = callback;
    }

    /**
     * 兼容旧代码的构造函数（不推荐使用）
     */
    @Deprecated
    public RxUploader(int maxRetries,
                      long baseRetryInterval,
                      double backoffFactor,
                      UploadCallback callback) {
        this(baseRetryInterval, backoffFactor,
                0, 0, // 默认不启用重置功能
                Schedulers.io(), callback);
    }

    protected abstract boolean doUpload(T data) throws Exception;

    public void uploadData(T data) {
        disposeCurrent();
        totalAttempts.set(0);
        cycleAttempts.set(0);

        disposable = createUploadObservable(data)
                .retryWhen(errors ->
                        errors.flatMap(error -> {
                            int total = totalAttempts.incrementAndGet();
                            int cycleAttempt = cycleAttempts.incrementAndGet();

                            // 检查是否达到重置条件
                            if (resetAfterAttempts > 0 && cycleAttempt >= resetAfterAttempts) {
                                return handleReset();
                            }

                            // 计算当前重试延迟
                            long delay = calculateDelay(cycleAttempt);
                            notifyRetry(total, delay);

                            return Observable.timer(delay, TimeUnit.MILLISECONDS);
                        })
                )
                .subscribeOn(workScheduler)
                .observeOn(workScheduler)
                .subscribe(
                        success -> notifySuccess(),
                        error -> notifyFailure(error.getMessage())
                );
    }

    /**
     * 创建上传Observable
     */
    private Observable<Boolean> createUploadObservable(T data) {
        return Observable.create(emitter -> {
            try {
                boolean success = doUpload(data);
                if (success) {
                    emitter.onNext(true);
                    emitter.onComplete();
                } else {
                    emitter.onError(new UploadFailedException("Upload returned false"));
                }
            } catch (Exception e) {
                emitter.onError(e);
            }
        });
    }

    /**
     * 处理重置逻辑
     */
    private Observable<Long> handleReset() {
        // 通知将要重置
        notifyReset(resetInterval);

        // 重置当前周期尝试次数
        cycleAttempts.set(0);

        // 返回等待重置间隔的Observable
        return Observable.timer(resetInterval, TimeUnit.MILLISECONDS);
    }

    /**
     * 计算延迟时间
     */
    private long calculateDelay(int cycleAttempt) {
        // 应用指数退避公式
        long delay = (long) (baseRetryInterval * Math.pow(backoffFactor, cycleAttempt - 1));

        // 添加随机抖动 (±15%)
        long jitter = (long) (delay * 0.15 * (Math.random() * 2 - 1));
        return Math.max(100, delay + jitter); // 确保最小延迟100ms
    }

    /**
     * 取消当前上传任务
     */
    public void cancel() {
        disposeCurrent();
    }

    private void disposeCurrent() {
        if (disposable != null && !disposable.isDisposed()) {
            disposable.dispose();
        }
    }

    private void notifySuccess() {
        if (callback != null) {
            callback.onSuccess();
        }
    }

    private void notifyFailure(String error) {
        if (callback != null) {
            callback.onFailure(error);
        }
    }

    private void notifyRetry(int attempt, long nextRetryDelay) {
        if (callback != null) {
            callback.onRetry(attempt, nextRetryDelay);
        }
    }

    private void notifyReset(long resetDelay) {
        if (callback != null) {
            callback.onReset(resetDelay);
        }
    }

    // 自定义异常类
    private static class UploadFailedException extends Exception {
        UploadFailedException(String message) {
            super(message);
        }
    }
}