package cn.forevermc.launcher.service;

import cn.forevermc.launcher.bean.Config;
import cn.forevermc.launcher.util.Resources;
import com.imyeyu.fx.task.RunAsync;
import com.imyeyu.inject.annotation.Inject;
import com.imyeyu.inject.annotation.InvokeForInjected;
import com.imyeyu.inject.annotation.Service;
import com.imyeyu.io.IO;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.CallbackArg;
import com.imyeyu.network.FileRequest;
import com.imyeyu.utils.OS;
import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener;
import javafx.collections.ObservableMap;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 夜雨
 * @since 2024-04-29 20:46
 */
@Slf4j
@Service
public class BGMService implements OS.FileSystem {

	private static final String CACHE_PATH = "etc" + SEP + "bgm";

	@Inject
	private Config config;

	@Inject
	private APISettingService apiSettingService;

	private final ObservableMap<String, Media> map;
	private final Map<String, File> cacheList = new HashMap<>();

	@Getter
	private MediaPlayer player;

	private Media bgmOld;
	private Runnable bgmRun;

	public BGMService() {
		map = FXCollections.observableHashMap();

		bgmRun = () -> {
			if (player != null) {
				player.dispose();
				player = null;
			}
			List<Media> list = new ArrayList<>(map.values());
			Collections.shuffle(list);
			Media bgmNew = list.getFirst();
			if (bgmOld != null) {
				while (bgmOld.equals(bgmNew)) {
					Collections.shuffle(list);
					bgmNew = list.getFirst();
				}
			}
			bgmOld = bgmNew;

			MediaPlayer player = new MediaPlayer(bgmNew);
			player.volumeProperty().bind(config.getMain().getBgmVolume().divide(100));
			player.setOnEndOfMedia(bgmRun);
			player.setOnReady(player::play);
		};

		map.addListener((MapChangeListener<String, Media>) change -> {
			if (player == null && !map.isEmpty()) {
//				bgmRun.run();
			}
		});
	}

	@InvokeForInjected
	private void injected() {
		try {
			{
				File[] files = IO.dir(CACHE_PATH).listFiles();
				if (files != null) {
					for (int i = 0; i < files.length; i++) {
						cacheList.put(files[i].getName(), files[i]);
					}
				}
			}
			if (TimiJava.isNotEmpty(cacheList)) {
				List<File> files = new ArrayList<>(cacheList.values());
				Collections.shuffle(files);
				map.put(files.getFirst().getName(), new Media(files.getFirst().toURI().toURL().toExternalForm()));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		apiSettingService.valueProperty().addListener((obs, o, setting) -> {
			if (setting == null) {
				return;
			}
			try {
				String active = setting.getBgm().getActive();
				if (TimiJava.isNotEmpty(active)) {
					if (cacheList.containsKey(active)) {
						map.put(active, new Media(cacheList.get(active).toURI().toURL().toExternalForm()));
					} else {
						fetch(active, active, value -> map.put(active, value));
					}
				} else {
					List<String> list = setting.getBgm().getList();
					if (TimiJava.isNotEmpty(list)){
						Collections.shuffle(list);
						String shuffleMongoId = list.getFirst();
						if (cacheList.containsKey(shuffleMongoId)) {
							map.put(shuffleMongoId, new Media(cacheList.get(shuffleMongoId).toURI().toURL().toExternalForm()));
						} else {
							fetch(shuffleMongoId, shuffleMongoId, value -> map.put(shuffleMongoId, value));
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		});
	}

	private void fetch(String mongoId, String fileName, CallbackArg<Media> callback) {
		new RunAsync<Media>() {

			@Override
			protected Media call() throws Exception {
				File file = IO.file(CACHE_PATH + SEP + fileName + ".mp3");
				FileRequest.get(Resources.TIMI_SERVER_API + "/attachment/read/" + mongoId).toFile(file);
				cacheList.put(mongoId, file);
				return new Media(file.toURI().toURL().toExternalForm());
			}

			@Override
			protected void onFinish(Media media) {
				callback.handler(media);
			}
		}.start();
	}
}
