use std::{mem, time::{Duration, Instant}};

use anyhow::Result;
use futures::{StreamExt, stream::FuturesUnordered};
use hashbrown::HashSet;
use yazi_core::mgr::OpenOpt;
use yazi_fs::{File, FsScheme, provider::{Provider, local::Local}};
use yazi_macro::succ;
use yazi_parser::mgr::DownloadForm;
use yazi_proxy::MgrProxy;
use yazi_shared::{data::Data, url::{UrlBuf, UrlLike}};
use yazi_vfs::VfsFile;

use crate::{Actor, Ctx};

pub struct Download;

impl Actor for Download {
	type Form = DownloadForm;

	const NAME: &str = "download";

	fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
		let cwd = cx.cwd().clone();
		let scheduler = cx.tasks.scheduler.clone();

		tokio::spawn(async move {
			Self::prepare(&form.urls).await;

			let mut wg1 = FuturesUnordered::new();
			for url in form.urls {
				let done = scheduler.file_download(url.clone());
				wg1.push(async move { (done.future().await, url) });
			}

			let mut wg2 = vec![];
			let mut urls = Vec::with_capacity(wg1.len());
			let mut files = Vec::with_capacity(wg1.len());
			let mut instant = Instant::now();
			while let Some((success, url)) = wg1.next().await {
				if !success {
					continue;
				}

				let Ok(f) = File::new(&url).await else { continue };
				urls.push(url);
				files.push(f);

				if instant.elapsed() >= Duration::from_secs(1) {
					wg2.push(scheduler.fetch_mimetype(mem::take(&mut files)));
					instant = Instant::now();
				}
			}

			if !files.is_empty() {
				wg2.push(scheduler.fetch_mimetype(files));
			}
			if futures::future::join_all(wg2).await.into_iter().any(|b| !b) {
				return;
			}
			if form.open && !urls.is_empty() {
				MgrProxy::open(OpenOpt {
					cwd:         Some(cwd),
					targets:     urls,
					interactive: false,
					hovered:     false,
				});
			}
		});

		succ!();
	}
}

impl Download {
	async fn prepare(urls: &[UrlBuf]) {
		let roots: HashSet<_> = urls.iter().filter_map(|u| u.scheme().cache()).collect();
		for mut root in roots {
			root.push("%lock");
			Local::regular(&root).create_dir_all().await.ok();
		}
	}
}
