use mlua::{FromLua, IntoLua, Lua, Table, Value};
use tokio::sync::mpsc;
use yazi_config::{KEYMAP, keymap::{ChordArc, Key}};
use yazi_macro::impl_data_any;
use yazi_shared::{Layer, event::ActionCow};

#[derive(Clone, Debug)]
pub struct WhichOpt {
	pub tx:     Option<mpsc::UnboundedSender<Option<ChordArc>>>,
	pub layer:  Layer,
	pub cands:  Vec<ChordArc>,
	pub times:  usize,
	pub silent: bool,
}

impl_data_any!(WhichOpt);

impl TryFrom<ActionCow> for WhichOpt {
	type Error = anyhow::Error;

	fn try_from(mut a: ActionCow) -> Result<Self, Self::Error> {
		if let Some(opt) = a.take_any2("opt") {
			return opt;
		}

		Ok(Self {
			tx:     a.take_any2("tx").transpose()?,
			layer:  a.str("layer").parse()?,
			cands:  a.take_any_iter().collect(),
			times:  a.get("times").unwrap_or(0),
			silent: a.bool("silent"),
		})
	}
}

impl WhichOpt {
	pub fn new(src: Layer, dist: Layer, key: Key) -> Self {
		Self {
			tx:     None,
			layer:  dist,
			cands:  KEYMAP
				.chords(src)
				.iter()
				.filter(|&c| c.on.len() > 1 && c.on[0] == key)
				.cloned()
				.collect(),
			times:  1,
			silent: false,
		}
	}
}

impl FromLua for WhichOpt {
	fn from_lua(value: Value, lua: &Lua) -> mlua::Result<Self> {
		let t = Table::from_lua(value, lua)?;

		Ok(Self {
			tx:     t.raw_get::<yazi_binding::MpscUnboundedTx<_>>("tx").ok().map(|t| t.0),
			layer:  t.raw_get("layer")?,
			cands:  t.raw_get::<Table>("cands")?.sequence_values().collect::<mlua::Result<Vec<_>>>()?,
			times:  t.raw_get("times").unwrap_or_default(),
			silent: t.raw_get("silent")?,
		})
	}
}

impl IntoLua for WhichOpt {
	#[rustfmt::skip]
	fn into_lua(self, lua: &Lua) -> mlua::Result<Value> {
		lua
			.create_table_from([
				("tx", self.tx.map(yazi_binding::MpscUnboundedTx).into_lua(lua)?),
				("layer", self.layer.into_lua(lua)?),
				("cands", lua.create_sequence_from(self.cands)?.into_lua(lua)?),
				("times", self.times.into_lua(lua)?),
				("silent", self.silent.into_lua(lua)?),
			])?
			.into_lua(lua)
	}
}
