package cn.forevermc.launcher.core;

import cn.forevermc.launcher.bean.FileDownload;
import cn.forevermc.launcher.ctrl.pane.ResourceDownload;
import com.imyeyu.fx.task.RunAsync;
import com.imyeyu.inject.annotation.Inject;
import com.imyeyu.inject.annotation.StaticInject;
import com.imyeyu.io.IOSpeedService;
import com.imyeyu.java.bean.Callback;
import com.imyeyu.java.bean.CallbackArg;
import com.imyeyu.java.bean.CallbackReturn;
import com.imyeyu.network.ProgressiveRequest;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.util.Timeout;

import java.util.List;

/**
 * 下载线程，返回为当前下载文件，内部调度
 *
 * @author 夜雨
 * @since 2022-11-21 13:27
 */
@Slf4j
@StaticInject
public class DownloadThread extends RunAsync<FileDownload> {

	@Inject
	private static ResourceDownload resourceDownload;

	@Inject
	private static IOSpeedService.Item ioSpeedItem;

	/** 线程 ID */
	private static int id = 0;

	/** 完成事件 */
	@Setter
	private CallbackArg<FileDownload> onFinishEvent;

	/** 错误事件 */
	@Setter
	private CallbackArg<FileDownload> onErrorEvent;

	/** 结束事件 */
	@Setter
	private Callback onFinallyEvent;

	private final String name;
	private final CallbackReturn<FileDownload> fetchFileHandler;

	/** true 为中断 */
	private boolean isInterrupted = false;

	/** 当前下载对象 */
	private FileDownload download;

	/**
	 * 默认构造器
	 *
	 * @param fetchFileHandler 任务获取回调，此接口不返回空时将持续进行下载文件，返回空时自然结束，非 FX 线程触发
	 */
	public DownloadThread(CallbackReturn<FileDownload> fetchFileHandler) {
		this.fetchFileHandler = fetchFileHandler;
		name = "[Download Service-%s] ".formatted(id++);
	}

	@Override
	protected FileDownload call() throws Exception {
		if (isInterrupted) {
			return null;
		}
		progress(0);
		if (download != null) {
			log.info("{}{}", name, download.getUrl());

			long size;
			if (download.getSize() == null) {
				size = ProgressiveRequest.wrap(Request.head(download.getUrl()).connectTimeout(Timeout.ofSeconds(6))).length();
			} else {
				size = download.getSize();
			}
			// 下载文件
			ProgressiveRequest.get(download.getUrl(), (total, read, now) -> {
				ioSpeedItem.push(now);
				progress(1D * read / size);
				return !isInterrupted;
			}).toFile(download.getFile());
		}
		progress(1);
		return download;
	}

	@Override
	protected void onFinish(FileDownload file) {
		if (file != null) {
			onFinishEvent.handler(file);
		}
		if (isInterrupted) {
			log.info(name + " died");
			return;
		}
		// 继续从回调接口获取并重新开始
		download = fetchFileHandler.handler();
		if (download != null) {
			download.progressProperty().bind(progressProperty());
			restart();
		} else {
			log.info(name + " died");
		}
	}

	@Override
	protected void onException(Throwable e) {
		if (isInterrupted) {
			log.info(name + " died");
			return;
		}
		if (download.getRetry() < 8) {
			// 重试
			log.warn("download error " + e.getMessage() + " and retry: " + download.getUrl());
			download.setRetry(download.getRetry() + 1);
			resourceDownload.putDownloadDeque(List.of(download));
		} else {
			log.error("download fail: " + download.getUrl(), e);
			download.setError(true);
		}
		// TODO 最终错误常驻列表并说明下载失败
		onErrorEvent.handler(download);

		// 任务异常也需继续抽取队列继续下载
		download = fetchFileHandler.handler();
		if (download != null) {
			download.progressProperty().bind(progressProperty());
			restart();
		} else {
			log.info(name + " died");
		}
	}

	@Override
	protected void onFinally() {
		onFinallyEvent.handler();
	}

	@Override
	public void start() {
		isInterrupted = false;
		super.start();
	}

	@Override
	public void restart() {
		isInterrupted = false;
		super.restart();
	}

	@Override
	public boolean cancel() {
		isInterrupted = true;
		return super.cancel();
	}
}
