use anyhow::Result;
use yazi_core::tab::Folder;
use yazi_fs::{CWD, Files, FilesOp, cha::Cha};
use yazi_macro::{act, succ};
use yazi_parser::VoidForm;
use yazi_shared::{data::Data, url::{UrlBuf, UrlLike}};
use yazi_vfs::{VfsFiles, VfsFilesOp};
use yazi_watcher::MgrProxy;

use crate::{Actor, Ctx};

pub struct Refresh;

impl Actor for Refresh {
	type Form = VoidForm;

	const NAME: &str = "refresh";

	fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
		CWD.set(cx.cwd(), Self::cwd_changed);

		if let Some(p) = cx.parent() {
			Self::trigger_dirs(&[cx.current(), p]);
		} else {
			Self::trigger_dirs(&[cx.current()]);
		}

		act!(mgr:peek, cx)?;
		act!(mgr:watch, cx)?;
		act!(mgr:update_paged, cx)?;

		cx.tasks.prework_sorted(&cx.current().files);
		succ!();
	}
}

impl Refresh {
	fn cwd_changed() {
		if CWD.load().kind().is_virtual() {
			MgrProxy::watch();
		}
	}

	// TODO: performance improvement
	fn trigger_dirs(folders: &[&Folder]) {
		async fn go(dir: UrlBuf, cha: Cha) {
			let Some(cha) = Files::assert_stale(&dir, cha).await else { return };

			match Files::from_dir_bulk(&dir).await {
				Ok(files) => FilesOp::Full(dir, files, cha).emit(),
				Err(e) => FilesOp::issue_error(&dir, e).await,
			}
		}

		let futs: Vec<_> = folders
			.iter()
			.filter(|&f| f.url.is_absolute() && f.url.is_internal())
			.map(|&f| go(f.url.clone(), f.cha))
			.collect();

		if !futs.is_empty() {
			tokio::spawn(futures::future::join_all(futs));
		}
	}
}
