use crate::err::Error;
use crate::sql::value::Value;
use crate::sql::{Array, Bytes, Datetime, Duration, Kind, Number, Regex, Strand, Thing};
pub trait FromArg: Sized {
	fn from_arg(arg: Value) -> Result<Self, Error>;
}
impl FromArg for Value {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		Ok(arg)
	}
}
impl FromArg for Regex {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_regex()
	}
}
impl FromArg for String {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_string()
	}
}
impl FromArg for Strand {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_strand()
	}
}
impl FromArg for Number {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_number()
	}
}
impl FromArg for Datetime {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_datetime()
	}
}
impl FromArg for Duration {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_duration()
	}
}
impl FromArg for Thing {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_record()
	}
}
impl FromArg for Array {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_array()
	}
}
impl FromArg for Bytes {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_bytes()
	}
}
impl FromArg for i64 {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_i64()
	}
}
impl FromArg for u64 {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_u64()
	}
}
impl FromArg for f64 {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_f64()
	}
}
impl FromArg for isize {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		Ok(arg.coerce_to_i64()? as isize)
	}
}
impl FromArg for usize {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		Ok(arg.coerce_to_u64()? as usize)
	}
}
impl FromArg for Vec<String> {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_array_type(&Kind::String)?.into_iter().map(Value::try_into).collect()
	}
}
impl FromArg for Vec<Number> {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_array_type(&Kind::Number)?.into_iter().map(Value::try_into).collect()
	}
}
impl FromArg for Vec<Datetime> {
	fn from_arg(arg: Value) -> Result<Self, Error> {
		arg.coerce_to_array_type(&Kind::Datetime)?.into_iter().map(Value::try_into).collect()
	}
}
pub trait FromArgs: Sized {
			fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error>;
}
impl FromArgs for Vec<Value> {
	fn from_args(_name: &str, args: Vec<Value>) -> Result<Self, Error> {
		Ok(args)
	}
}
impl FromArgs for Vec<Array> {
	fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
		args.into_iter()
			.enumerate()
			.map(|(i, arg)| {
				arg.coerce_to_array_type(&Kind::Any).map_err(|e| Error::InvalidArguments {
					name: name.to_owned(),
					message: format!("Argument {} was the wrong type. {e}", i + 1),
				})
			})
			.collect()
	}
}
macro_rules! impl_tuple {
	($len:expr, $( $T:ident ),*) => {
		impl<$($T:FromArg),*> FromArgs for ($($T,)*) {
			#[allow(non_snake_case)]
			fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
				let [$($T),*]: [Value; $len] = args.try_into().map_err(|_| Error::InvalidArguments {
					name: name.to_owned(),
										message: match $len {
						0 => String::from("Expected no arguments."),
						1 => String::from("Expected 1 argument."),
						_ => format!("Expected {} arguments.", $len),
					}
				})?;
				#[allow(unused_mut, unused_variables)]
				let mut i = 0;
				Ok((
					$({
						i += 1;
						$T::from_arg($T).map_err(|e| Error::InvalidArguments {
							name: name.to_owned(),
							message: format!("Argument {i} was the wrong type. {e}"),
						})?
					},)*
				))
			}
		}
	}
}
impl_tuple!(0,);
impl_tuple!(1, A);
impl_tuple!(2, A, B);
impl_tuple!(3, A, B, C);
impl<A: FromArg> FromArgs for (Option<A>,) {
	fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
		let err = || Error::InvalidArguments {
			name: name.to_owned(),
			message: String::from("Expected 0 or 1 arguments."),
		};
				let mut args = args.into_iter();
				let a = match args.next() {
			Some(a) => Some(A::from_arg(a).map_err(|e| Error::InvalidArguments {
				name: name.to_owned(),
				message: format!("Argument 1 was the wrong type. {e}"),
			})?),
			None => None,
		};
				if args.next().is_some() {
						return Err(err());
		}
		Ok((a,))
	}
}
impl<A: FromArg, B: FromArg> FromArgs for (A, Option<B>) {
	fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
		let err = || Error::InvalidArguments {
			name: name.to_owned(),
			message: String::from("Expected 1 or 2 arguments."),
		};
				let mut args = args.into_iter();
				let a = A::from_arg(args.next().ok_or_else(err)?).map_err(|e| Error::InvalidArguments {
			name: name.to_owned(),
			message: format!("Argument 1 was the wrong type. {e}"),
		})?;
		let b = match args.next() {
			Some(b) => Some(B::from_arg(b)?),
			None => None,
		};
				if args.next().is_some() {
						return Err(err());
		}
		Ok((a, b))
	}
}
impl<A: FromArg, B: FromArg, C: FromArg> FromArgs for (A, B, Option<C>) {
	fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
		let err = || Error::InvalidArguments {
			name: name.to_owned(),
			message: String::from("Expected 2 or 3 arguments."),
		};
				let mut args = args.into_iter();
				let a = A::from_arg(args.next().ok_or_else(err)?).map_err(|e| Error::InvalidArguments {
			name: name.to_owned(),
			message: format!("Argument 1 was the wrong type. {e}"),
		})?;
				let b = B::from_arg(args.next().ok_or_else(err)?).map_err(|e| Error::InvalidArguments {
			name: name.to_owned(),
			message: format!("Argument 2 was the wrong type. {e}"),
		})?;
				let c = match args.next() {
			Some(c) => Some(C::from_arg(c).map_err(|e| Error::InvalidArguments {
				name: name.to_owned(),
				message: format!("Argument 3 was the wrong type. {e}"),
			})?),
			None => None,
		};
				if args.next().is_some() {
						return Err(err());
		}
		Ok((a, b, c))
	}
}
impl<A: FromArg, B: FromArg> FromArgs for (Option<A>, Option<B>) {
	fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
		let err = || Error::InvalidArguments {
			name: name.to_owned(),
			message: String::from("Expected 0, 1, or 2 arguments."),
		};
				let mut args = args.into_iter();
				let a = match args.next() {
			Some(a) => Some(A::from_arg(a).map_err(|e| Error::InvalidArguments {
				name: name.to_owned(),
				message: format!("Argument 1 was the wrong type. {e}"),
			})?),
			None => None,
		};
				let b = match args.next() {
			Some(b) => Some(B::from_arg(b).map_err(|e| Error::InvalidArguments {
				name: name.to_owned(),
				message: format!("Argument 2 was the wrong type. {e}"),
			})?),
			None => None,
		};
				if args.next().is_some() {
						return Err(err());
		}
		Ok((a, b))
	}
}
impl<A: FromArg, B: FromArg> FromArgs for (Option<(A, B)>,) {
	fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
		let err = || Error::InvalidArguments {
			name: name.to_owned(),
			message: String::from("Expected 0 or 2 arguments."),
		};
				let mut args = args.into_iter();
				let a = match args.next() {
			Some(a) => Some(A::from_arg(a).map_err(|e| Error::InvalidArguments {
				name: name.to_owned(),
				message: format!("Argument 1 was the wrong type. {e}"),
			})?),
			None => None,
		};
				let b = match args.next() {
			Some(b) => Some(B::from_arg(b).map_err(|e| Error::InvalidArguments {
				name: name.to_owned(),
				message: format!("Argument 2 was the wrong type. {e}"),
			})?),
			None => None,
		};
				if a.is_some() != b.is_some() || args.next().is_some() {
						return Err(err());
		}
		Ok((a.zip(b),))
	}
}
impl<A: FromArg, B: FromArg, C: FromArg> FromArgs for (A, Option<B>, Option<C>) {
	fn from_args(name: &str, args: Vec<Value>) -> Result<Self, Error> {
		let err = || Error::InvalidArguments {
			name: name.to_owned(),
			message: String::from("Expected 1, 2, or 3 arguments."),
		};
				let mut args = args.into_iter();
				let a = A::from_arg(args.next().ok_or_else(err)?).map_err(|e| Error::InvalidArguments {
			name: name.to_owned(),
			message: format!("Argument 1 was the wrong type. {e}"),
		})?;
				let b = match args.next() {
			Some(b) => Some(B::from_arg(b).map_err(|e| Error::InvalidArguments {
				name: name.to_owned(),
				message: format!("Argument 2 was the wrong type. {e}"),
			})?),
			None => None,
		};
				let c = match args.next() {
			Some(c) => Some(C::from_arg(c).map_err(|e| Error::InvalidArguments {
				name: name.to_owned(),
				message: format!("Argument 3 was the wrong type. {e}"),
			})?),
			None => None,
		};
				if args.next().is_some() {
						return Err(err());
		}
		Ok((a, b, c))
	}
}