use crate::cnf::GENERATION_ALLOCATION_LIMIT;
use crate::ctx::Context;
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::array::Array;
use crate::sql::array::Clump;
use crate::sql::array::Combine;
use crate::sql::array::Complement;
use crate::sql::array::Difference;
use crate::sql::array::Flatten;
use crate::sql::array::Intersect;
use crate::sql::array::Matches;
use crate::sql::array::Transpose;
use crate::sql::array::Union;
use crate::sql::array::Uniq;
use crate::sql::array::Windows;
use crate::sql::value::Value;
use crate::sql::Closure;
use crate::sql::Function;
use rand::prelude::SliceRandom;
use reblessive::tree::Stk;
use std::mem::size_of_val;
fn limit(name: &str, n: usize) -> Result<(), Error> {
	if n > *GENERATION_ALLOCATION_LIMIT {
		Err(Error::InvalidArguments {
			name: name.to_owned(),
			message: format!("Output must not exceed {} bytes.", *GENERATION_ALLOCATION_LIMIT),
		})
	} else {
		Ok(())
	}
}
pub fn add((mut array, value): (Array, Value)) -> Result<Value, Error> {
	match value {
		Value::Array(value) => {
			for v in value.0 {
				if !array.0.iter().any(|x| *x == v) {
					array.0.push(v)
				}
			}
			Ok(array.into())
		}
		value => {
			if !array.0.iter().any(|x| *x == value) {
				array.0.push(value)
			}
			Ok(array.into())
		}
	}
}
pub async fn all(
	(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
	(array, check): (Array, Option<Value>),
) -> Result<Value, Error> {
	Ok(match check {
		Some(closure) if closure.is_closure() => {
			if let Some(opt) = opt {
				for val in array.iter() {
					let arg = val.compute(stk, ctx, opt, doc).await?;
					let fnc = Function::Anonymous(closure.clone(), vec![arg], true);
					if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
						continue;
					} else {
						return Ok(Value::Bool(false));
					}
				}
				Value::Bool(true)
			} else {
				Value::None
			}
		}
		Some(value) => array.iter().all(|v: &Value| *v == value).into(),
		None => array.iter().all(Value::is_truthy).into(),
	})
}
pub async fn any(
	(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
	(array, check): (Array, Option<Value>),
) -> Result<Value, Error> {
	Ok(match check {
		Some(closure) if closure.is_closure() => {
			if let Some(opt) = opt {
				for val in array.iter() {
					let arg = val.compute(stk, ctx, opt, doc).await?;
					let fnc = Function::Anonymous(closure.clone(), vec![arg], true);
					if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
						return Ok(Value::Bool(true));
					} else {
						continue;
					}
				}
				Value::Bool(false)
			} else {
				Value::None
			}
		}
		Some(value) => array.iter().any(|v: &Value| *v == value).into(),
		None => array.iter().any(Value::is_truthy).into(),
	})
}
pub fn append((mut array, value): (Array, Value)) -> Result<Value, Error> {
	array.push(value);
	Ok(array.into())
}
pub fn at((array, i): (Array, i64)) -> Result<Value, Error> {
	let mut idx = i as usize;
	if i < 0 {
		idx = (array.len() as i64 + i) as usize;
	}
	Ok(array.get(idx).cloned().unwrap_or_default())
}
pub fn boolean_and((lh, rh): (Array, Array)) -> Result<Value, Error> {
	let longest_length = lh.len().max(rh.len());
	let mut results = Array::with_capacity(longest_length);
	for i in 0..longest_length {
		let lhv = lh.get(i);
		let rhv = rh.get(i);
		results
			.push((lhv.is_some_and(Value::is_truthy) && rhv.is_some_and(Value::is_truthy)).into());
	}
	Ok(results.into())
}
pub fn boolean_not((mut array,): (Array,)) -> Result<Value, Error> {
	array.iter_mut().for_each(|v| *v = (!v.is_truthy()).into());
	Ok(array.into())
}
pub fn boolean_or((lh, rh): (Array, Array)) -> Result<Value, Error> {
	let longest_length = lh.len().max(rh.len());
	let mut results = Array::with_capacity(longest_length);
	for i in 0..longest_length {
		let lhv = lh.get(i);
		let rhv = rh.get(i);
		results
			.push((lhv.is_some_and(Value::is_truthy) || rhv.is_some_and(Value::is_truthy)).into());
	}
	Ok(results.into())
}
pub fn boolean_xor((lh, rh): (Array, Array)) -> Result<Value, Error> {
	let longest_length = lh.len().max(rh.len());
	let mut results = Array::with_capacity(longest_length);
	for i in 0..longest_length {
		let lhv = lh.get(i);
		let rhv = rh.get(i);
		results
			.push((lhv.is_some_and(Value::is_truthy) ^ rhv.is_some_and(Value::is_truthy)).into());
	}
	Ok(results.into())
}
pub fn clump((array, clump_size): (Array, i64)) -> Result<Value, Error> {
	let clump_size = clump_size.max(0) as usize;
	Ok(array.clump(clump_size)?.into())
}
pub fn combine((array, other): (Array, Array)) -> Result<Value, Error> {
	Ok(array.combine(other).into())
}
pub fn complement((array, other): (Array, Array)) -> Result<Value, Error> {
	Ok(array.complement(other).into())
}
pub fn concat(mut arrays: Vec<Array>) -> Result<Value, Error> {
	let len = match arrays.iter().map(Array::len).reduce(|c, v| c + v) {
		None => Err(Error::InvalidArguments {
			name: String::from("array::concat"),
			message: String::from("Expected at least one argument"),
		}),
		Some(l) => Ok(l),
	}?;
	let mut arr = Array::with_capacity(len);
	arrays.iter_mut().for_each(|val| {
		arr.append(val);
	});
	Ok(arr.into())
}
pub fn difference((array, other): (Array, Array)) -> Result<Value, Error> {
	Ok(array.difference(other).into())
}
pub fn distinct((array,): (Array,)) -> Result<Value, Error> {
	Ok(array.uniq().into())
}
pub fn fill(
	(mut array, value, start, end): (Array, Value, Option<isize>, Option<isize>),
) -> Result<Value, Error> {
	let min = 0;
	let max = array.len();
	let negative_max = -(max as isize);
	let start = match start {
		Some(start) if negative_max <= start && start < 0 => (start + max as isize) as usize,
		Some(start) if start < negative_max => 0,
		Some(start) => start as usize,
		None => min,
	};
	let end = match end {
		Some(end) if negative_max <= end && end < 0 => (end + max as isize) as usize,
		Some(end) if end < negative_max => 0,
		Some(end) => end as usize,
		None => max,
	};
	if start == min && end >= max {
		array.fill(value);
	} else if end > start {
		let end_minus_one = end - 1;
		for i in start..end_minus_one {
			if let Some(elem) = array.get_mut(i) {
				*elem = value.clone();
			}
		}
		if let Some(last_elem) = array.get_mut(end_minus_one) {
			*last_elem = value;
		}
	}
	Ok(array.into())
}
pub async fn filter(
	(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
	(array, check): (Array, Value),
) -> Result<Value, Error> {
	Ok(match check {
		closure if closure.is_closure() => {
			if let Some(opt) = opt {
				let mut res = Vec::with_capacity(array.len());
				for val in array.iter() {
					let arg = val.compute(stk, ctx, opt, doc).await?;
					let fnc = Function::Anonymous(closure.clone(), vec![arg.clone()], true);
					if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
						res.push(arg)
					}
				}
				Value::from(res)
			} else {
				Value::None
			}
		}
		value => array.into_iter().filter(|v: &Value| *v == value).collect::<Vec<_>>().into(),
	})
}
pub async fn filter_index(
	(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
	(array, value): (Array, Value),
) -> Result<Value, Error> {
	Ok(match value {
		closure if closure.is_closure() => {
			if let Some(opt) = opt {
				let mut res = Vec::with_capacity(array.len());
				for (i, val) in array.iter().enumerate() {
					let arg = val.compute(stk, ctx, opt, doc).await?;
					let fnc = Function::Anonymous(closure.clone(), vec![arg, i.into()], true);
					if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
						res.push(i);
					}
				}
				Value::from(res)
			} else {
				Value::None
			}
		}
		value => array
			.iter()
			.enumerate()
			.filter_map(|(i, v)| {
				if *v == value {
					Some(Value::from(i))
				} else {
					None
				}
			})
			.collect::<Vec<_>>()
			.into(),
	})
}
pub async fn find(
	(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
	(array, value): (Array, Value),
) -> Result<Value, Error> {
	Ok(match value {
		closure if closure.is_closure() => {
			if let Some(opt) = opt {
				for val in array.iter() {
					let arg = val.compute(stk, ctx, opt, doc).await?;
					let fnc = Function::Anonymous(closure.clone(), vec![arg.clone()], true);
					if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
						return Ok(arg);
					}
				}
				Value::None
			} else {
				Value::None
			}
		}
		value => array.into_iter().find(|v: &Value| *v == value).into(),
	})
}
pub async fn find_index(
	(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
	(array, value): (Array, Value),
) -> Result<Value, Error> {
	Ok(match value {
		closure if closure.is_closure() => {
			if let Some(opt) = opt {
				for (i, val) in array.iter().enumerate() {
					let arg = val.compute(stk, ctx, opt, doc).await?;
					let fnc = Function::Anonymous(closure.clone(), vec![arg, i.into()], true);
					if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
						return Ok(i.into());
					}
				}
				Value::None
			} else {
				Value::None
			}
		}
		value => array
			.iter()
			.enumerate()
			.find_map(|(i, v)| {
				if *v == value {
					Some(Value::from(i))
				} else {
					None
				}
			})
			.into(),
	})
}
pub fn first((array,): (Array,)) -> Result<Value, Error> {
	if let [first, ..] = &array[0..] {
		Ok(first.to_owned())
	} else {
		Ok(Value::None)
	}
}
pub fn flatten((array,): (Array,)) -> Result<Value, Error> {
	Ok(array.flatten().into())
}
pub async fn fold(
	(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
	(array, init, mapper): (Array, Value, Closure),
) -> Result<Value, Error> {
	if let Some(opt) = opt {
		let mut accum = init;
		for (i, val) in array.into_iter().enumerate() {
			let fnc = Function::Anonymous(mapper.clone().into(), vec![accum, val, i.into()], true);
			accum = fnc.compute(stk, ctx, opt, doc).await?;
		}
		Ok(accum)
	} else {
		Ok(Value::None)
	}
}
pub fn group((array,): (Array,)) -> Result<Value, Error> {
	Ok(array.flatten().uniq().into())
}
pub fn insert((mut array, value, index): (Array, Value, Option<i64>)) -> Result<Value, Error> {
	match index {
		Some(mut index) => {
						if index < 0 {
				index += array.len() as i64;
			}
						if index > array.len() as i64 || index < 0 {
				return Ok(array.into());
			}
						array.insert(index as usize, value);
						Ok(array.into())
		}
		None => {
			array.push(value);
			Ok(array.into())
		}
	}
}
pub fn intersect((array, other): (Array, Array)) -> Result<Value, Error> {
	Ok(array.intersect(other).into())
}
pub fn is_empty((array,): (Array,)) -> Result<Value, Error> {
	Ok(array.is_empty().into())
}
pub fn join((arr, sep): (Array, String)) -> Result<Value, Error> {
	Ok(arr.into_iter().map(Value::as_raw_string).collect::<Vec<_>>().join(&sep).into())
}
pub fn last((array,): (Array,)) -> Result<Value, Error> {
	if let [.., last] = &array[0..] {
		Ok(last.to_owned())
	} else {
		Ok(Value::None)
	}
}
pub fn len((array,): (Array,)) -> Result<Value, Error> {
	Ok(array.len().into())
}
pub fn logical_and((lh, rh): (Array, Array)) -> Result<Value, Error> {
	let mut result_arr = Array::with_capacity(lh.len().max(rh.len()));
	let mut iters = (lh.into_iter(), rh.into_iter());
	for (lhv, rhv) in std::iter::from_fn(|| {
		let r = (iters.0.next(), iters.1.next());
		if r.0.is_none() && r.1.is_none() {
			None
		} else {
			Some((r.0.unwrap_or(Value::Null), r.1.unwrap_or(Value::Null)))
		}
	}) {
		let truth = lhv.is_truthy() && rhv.is_truthy();
		let r = if lhv.is_truthy() == truth {
			lhv
		} else if rhv.is_truthy() == truth {
			rhv
		} else {
			truth.into()
		};
		result_arr.push(r);
	}
	Ok(result_arr.into())
}
pub fn logical_or((lh, rh): (Array, Array)) -> Result<Value, Error> {
	let mut result_arr = Array::with_capacity(lh.len().max(rh.len()));
	let mut iters = (lh.into_iter(), rh.into_iter());
	for (lhv, rhv) in std::iter::from_fn(|| {
		let r = (iters.0.next(), iters.1.next());
		if r.0.is_none() && r.1.is_none() {
			None
		} else {
			Some((r.0.unwrap_or(Value::Null), r.1.unwrap_or(Value::Null)))
		}
	}) {
		let truth = lhv.is_truthy() || rhv.is_truthy();
		let r = if lhv.is_truthy() == truth {
			lhv
		} else if rhv.is_truthy() == truth {
			rhv
		} else {
			truth.into()
		};
		result_arr.push(r);
	}
	Ok(result_arr.into())
}
pub fn logical_xor((lh, rh): (Array, Array)) -> Result<Value, Error> {
	let mut result_arr = Array::with_capacity(lh.len().max(rh.len()));
	let mut iters = (lh.into_iter(), rh.into_iter());
	for (lhv, rhv) in std::iter::from_fn(|| {
		let r = (iters.0.next(), iters.1.next());
		if r.0.is_none() && r.1.is_none() {
			None
		} else {
			Some((r.0.unwrap_or(Value::Null), r.1.unwrap_or(Value::Null)))
		}
	}) {
		let truth = lhv.is_truthy() ^ rhv.is_truthy();
		let r = if lhv.is_truthy() == truth {
			lhv
		} else if rhv.is_truthy() == truth {
			rhv
		} else {
			truth.into()
		};
		result_arr.push(r);
	}
	Ok(result_arr.into())
}
pub async fn map(
	(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
	(array, mapper): (Array, Closure),
) -> Result<Value, Error> {
	if let Some(opt) = opt {
		let mut res = Vec::with_capacity(array.len());
		for (i, val) in array.into_iter().enumerate() {
			let arg = val.compute(stk, ctx, opt, doc).await?;
			let fnc = Function::Anonymous(mapper.clone().into(), vec![arg, i.into()], true);
			res.push(fnc.compute(stk, ctx, opt, doc).await?);
		}
		Ok(res.into())
	} else {
		Ok(Value::None)
	}
}
pub fn matches((array, compare_val): (Array, Value)) -> Result<Value, Error> {
	Ok(array.matches(compare_val).into())
}
pub fn max((array,): (Array,)) -> Result<Value, Error> {
	Ok(array.into_iter().max().unwrap_or_default())
}
pub fn min((array,): (Array,)) -> Result<Value, Error> {
	Ok(array.into_iter().min().unwrap_or_default())
}
pub fn pop((mut array,): (Array,)) -> Result<Value, Error> {
	Ok(array.pop().into())
}
pub fn prepend((mut array, value): (Array, Value)) -> Result<Value, Error> {
	array.insert(0, value);
	Ok(array.into())
}
pub fn push((mut array, value): (Array, Value)) -> Result<Value, Error> {
	array.push(value);
	Ok(array.into())
}
pub fn range((start, count): (i64, i64)) -> Result<Value, Error> {
	if count < 0 {
		return Err(Error::InvalidArguments {
			name: String::from("array::range"),
			message: format!(
				"Argument 1 was the wrong type. Expected a positive number but found {count}"
			),
		});
	}
	if let Some(end) = start.checked_add(count - 1) {
		Ok(Array((start..=end).map(Value::from).collect::<Vec<_>>()).into())
	} else {
		Err(Error::InvalidArguments {
			name: String::from("array::range"),
			message: String::from("The range overflowed the maximum value for an integer"),
		})
	}
}
pub async fn reduce(
	(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
	(array, mapper): (Array, Closure),
) -> Result<Value, Error> {
	if let Some(opt) = opt {
		match array.len() {
			0 => Ok(Value::None),
			1 => {
				let Some(val) = array.into_iter().next() else {
					return Err(Error::Unreachable(
						"Iterator should have an item at this point".into(),
					));
				};
				Ok(val)
			}
			_ => {
								let mut iter = array.into_iter();
				let Some(mut accum) = iter.next() else {
					return Ok(Value::None);
				};
				for (idx, val) in iter.enumerate() {
					let fnc = Function::Anonymous(
						mapper.clone().into(),
						vec![accum, val, idx.into()],
						true,
					);
					accum = fnc.compute(stk, ctx, opt, doc).await?;
				}
				Ok(accum)
			}
		}
	} else {
		Ok(Value::None)
	}
}
pub fn remove((mut array, mut index): (Array, i64)) -> Result<Value, Error> {
		if index < 0 {
		index += array.len() as i64;
	}
		if index >= array.len() as i64 || index < 0 {
		return Ok(array.into());
	}
		array.remove(index as usize);
		Ok(array.into())
}
pub fn repeat((value, count): (Value, usize)) -> Result<Value, Error> {
	limit("array::repeat", size_of_val(&value).saturating_mul(count))?;
	Ok(Array(std::iter::repeat(value).take(count).collect()).into())
}
pub fn reverse((mut array,): (Array,)) -> Result<Value, Error> {
	array.reverse();
	Ok(array.into())
}
pub fn shuffle((mut array,): (Array,)) -> Result<Value, Error> {
	let mut rng = rand::thread_rng();
	array.shuffle(&mut rng);
	Ok(array.into())
}
pub fn slice((array, beg, lim): (Array, Option<isize>, Option<isize>)) -> Result<Value, Error> {
	let skip = match beg {
		Some(v) if v < 0 => array.len().saturating_sub(v.unsigned_abs()),
		Some(v) => v as usize,
		None => 0,
	};
	let take = match lim {
		Some(v) if v < 0 => array.len().saturating_sub(skip).saturating_sub(v.unsigned_abs()),
		Some(v) => v as usize,
		None => usize::MAX,
	};
	Ok(if skip > 0 || take < usize::MAX {
		array.into_iter().skip(skip).take(take).collect::<Vec<_>>().into()
	} else {
		array
	}
	.into())
}
pub fn sort((mut array, order): (Array, Option<Value>)) -> Result<Value, Error> {
	match order {
				Some(Value::Strand(s)) if s.as_str() == "asc" => {
			array.sort_unstable();
			Ok(array.into())
		}
				Some(Value::Strand(s)) if s.as_str() == "desc" => {
			array.sort_unstable_by(|a, b| b.cmp(a));
			Ok(array.into())
		}
				Some(Value::Bool(true)) => {
			array.sort_unstable();
			Ok(array.into())
		}
				Some(Value::Bool(false)) => {
			array.sort_unstable_by(|a, b| b.cmp(a));
			Ok(array.into())
		}
				_ => {
			array.sort_unstable();
			Ok(array.into())
		}
	}
}
pub fn swap((mut array, from, to): (Array, isize, isize)) -> Result<Value, Error> {
	let min = 0;
	let max = array.len();
	let negative_max = -(max as isize);
	let from = match from {
		from if from < negative_max || from >= max as isize => Err(Error::InvalidArguments {
			name: String::from("array::swap"),
			message: format!(
				"Argument 1 is out of range. Expected a number between {negative_max} and {max}"
			),
		}),
		from if negative_max <= from && from < min => Ok((from + max as isize) as usize),
		from => Ok(from as usize),
	}?;
	let to = match to {
		to if to < negative_max || to >= max as isize => Err(Error::InvalidArguments {
			name: String::from("array::swap"),
			message: format!(
				"Argument 2 is out of range. Expected a number between {negative_max} and {max}"
			),
		}),
		to if negative_max <= to && to < min => Ok((to + max as isize) as usize),
		to => Ok(to as usize),
	}?;
	array.swap(from, to);
	Ok(array.into())
}
pub fn transpose((array,): (Array,)) -> Result<Value, Error> {
	Ok(array.transpose().into())
}
pub fn union((array, other): (Array, Array)) -> Result<Value, Error> {
	Ok(array.union(other).into())
}
pub fn windows((array, window_size): (Array, i64)) -> Result<Value, Error> {
	let window_size = window_size.max(0) as usize;
	Ok(array.windows(window_size)?.into())
}
pub mod sort {
	use crate::err::Error;
	use crate::sql::array::Array;
	use crate::sql::value::Value;
	pub fn asc((mut array,): (Array,)) -> Result<Value, Error> {
		array.sort_unstable();
		Ok(array.into())
	}
	pub fn desc((mut array,): (Array,)) -> Result<Value, Error> {
		array.sort_unstable_by(|a, b| b.cmp(a));
		Ok(array.into())
	}
}
#[cfg(test)]
mod tests {
	use super::{at, first, join, last, slice};
	use crate::sql::{Array, Value};
	#[test]
	fn array_slice() {
		fn test(initial: &[u8], beg: Option<isize>, lim: Option<isize>, expected: &[u8]) {
			let initial_values =
				initial.iter().map(|n| Value::from(*n as i64)).collect::<Vec<_>>().into();
			let expected_values: Array =
				expected.iter().map(|n| Value::from(*n as i64)).collect::<Vec<_>>().into();
			assert_eq!(slice((initial_values, beg, lim)).unwrap(), expected_values.into());
		}
		let array = b"abcdefg";
		test(array, None, None, array);
		test(array, Some(2), None, &array[2..]);
		test(array, Some(2), Some(3), &array[2..5]);
		test(array, Some(2), Some(-1), b"cdef");
		test(array, Some(-2), None, b"fg");
		test(array, Some(-4), Some(2), b"de");
		test(array, Some(-4), Some(-1), b"def");
	}
	#[test]
	fn array_join() {
		fn test(arr: Array, sep: &str, expected: &str) {
			assert_eq!(join((arr, sep.to_string())).unwrap(), expected.into());
		}
		test(Vec::<Value>::new().into(), ",", "");
		test(vec!["hello"].into(), ",", "hello");
		test(vec!["hello", "world"].into(), ",", "hello,world");
		test(vec!["again"; 512].into(), " and ", &vec!["again"; 512].join(" and "));
		test(
			vec![Value::from(true), Value::from(false), Value::from(true)].into(),
			" is ",
			"true is false is true",
		);
		test(
			vec![Value::from(3.56), Value::from(2.72), Value::from(1.61)].into(),
			" is not ",
			"3.56f is not 2.72f is not 1.61f",
		);
	}
	#[test]
	fn array_first() {
		fn test(arr: Array, expected: Value) {
			assert_eq!(first((arr,)).unwrap(), expected);
		}
		test(vec!["hello", "world"].into(), "hello".into());
		test(Array::new(), Value::None);
	}
	#[test]
	fn array_last() {
		fn test(arr: Array, expected: Value) {
			assert_eq!(last((arr,)).unwrap(), expected);
		}
		test(vec!["hello", "world"].into(), "world".into());
		test(Array::new(), Value::None);
	}
	#[test]
	fn array_at() {
		fn test(arr: Array, i: i64, expected: Value) {
			assert_eq!(at((arr, i)).unwrap(), expected);
		}
		test(vec!["hello", "world"].into(), -2, "hello".into());
		test(vec!["hello", "world"].into(), -3, Value::None);
	}
}