use crate::ctx::Context;
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::statements::info::InfoStructure;
use crate::sql::{fmt::Fmt, Idiom, Part, Value};
use crate::syn;
use reblessive::tree::Stk;
use revision::revisioned;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt::{self, Display, Formatter, Write};
use std::ops::Deref;
use super::paths::ID;
use super::FlowResultExt as _;
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub struct Fields(pub Vec<Field>, pub bool);
impl Fields {
		pub(crate) fn all() -> Self {
		Self(vec![Field::All], false)
	}
		pub fn is_all(&self) -> bool {
		self.0.iter().any(|v| matches!(v, Field::All))
	}
		pub(crate) fn value_id() -> Self {
		Self(
			vec![Field::Single {
				expr: Value::Idiom(Idiom(ID.to_vec())),
				alias: None,
			}],
			true,
		)
	}
		pub fn other(&self) -> impl Iterator<Item = &Field> {
		self.0.iter().filter(|v| !matches!(v, Field::All))
	}
		pub fn single(&self) -> Option<&Field> {
		match (self.0.len(), self.1) {
			(1, true) => match self.0.first() {
				Some(Field::All) => None,
				Some(v) => Some(v),
				_ => None,
			},
			_ => None,
		}
	}
		pub(crate) fn is_count_all_only(&self) -> bool {
		let mut is_count_only = false;
		for field in &self.0 {
			if let Field::Single {
				expr: Value::Function(func),
				..
			} = field
			{
				if func.is_count_all() {
					is_count_only = true;
					continue;
				}
			}
			return false;
		}
		is_count_only
	}
}
impl Deref for Fields {
	type Target = Vec<Field>;
	fn deref(&self) -> &Self::Target {
		&self.0
	}
}
impl IntoIterator for Fields {
	type Item = Field;
	type IntoIter = std::vec::IntoIter<Self::Item>;
	fn into_iter(self) -> Self::IntoIter {
		self.0.into_iter()
	}
}
impl Display for Fields {
	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
		match self.single() {
			Some(v) => write!(f, "VALUE {}", &v),
			None => Display::fmt(&Fmt::comma_separated(&self.0), f),
		}
	}
}
impl InfoStructure for Fields {
	fn structure(self) -> Value {
		self.to_string().into()
	}
}
impl Fields {
		pub(crate) async fn compute(
		&self,
		stk: &mut Stk,
		ctx: &Context,
		opt: &Options,
		doc: Option<&CursorDoc>,
		group: bool,
	) -> Result<Value, Error> {
		if let Some(doc) = doc {
			self.compute_value(stk, ctx, opt, doc, group).await
		} else {
			let doc = Value::None.into();
			self.compute_value(stk, ctx, opt, &doc, group).await
		}
	}
	async fn compute_value(
		&self,
		stk: &mut Stk,
		ctx: &Context,
		opt: &Options,
		doc: &CursorDoc,
		group: bool,
	) -> Result<Value, Error> {
				let opt = &opt.new_with_futures(true);
				let mut out = match self.is_all() {
			true => doc.doc.as_ref().compute(stk, ctx, opt, Some(doc)).await.catch_return()?,
			false => Value::base(),
		};
		for v in self.other() {
			match v {
				Field::All => (),
				Field::Single {
					expr,
					alias,
				} => {
					let name = alias
						.as_ref()
						.map(Cow::Borrowed)
						.unwrap_or_else(|| Cow::Owned(expr.to_idiom()));
					match expr {
												Value::Function(f) if group && f.is_aggregate() => {
							let x = match f.args().len() {
																0 => f.compute(stk, ctx, opt, Some(doc)).await.catch_return()?,
																_ => f.args()[0]
									.compute(stk, ctx, opt, Some(doc))
									.await
									.catch_return()?,
							};
														match self.single().is_some() {
								false => out.set(stk, ctx, opt, name.as_ref(), x).await?,
								true => out = x,
							}
						}
												Value::Idiom(v) if v.is_multi_yield() => {
														let mut res: Vec<(&[Part], Value)> = Vec::new();
														for v in v.split_inclusive(Idiom::split_multi_yield) {
																let x = match res.last() {
									Some((_, r)) => r,
									None => doc.doc.as_ref(),
								};
																let x = x
									.get(stk, ctx, opt, Some(doc), v)
									.await
									.catch_return()?
									.compute(stk, ctx, opt, Some(doc))
									.await
																											.catch_return()?
									.flatten();
																res.push((v, x));
							}
														for (p, x) in res {
								match p.last().unwrap().alias() {
																		Some(a) => {
										if let Some(i) = alias {
											out.set(stk, ctx, opt, i, x.clone()).await?;
										}
										out.set(stk, ctx, opt, a, x).await?;
									}
																		None => {
										out.set(stk, ctx, opt, alias.as_ref().unwrap_or(v), x)
											.await?
									}
								}
							}
						}
												Value::Function(f) if f.name() == Some("type::fields") => {
														let expr =
								expr.compute(stk, ctx, opt, Some(doc)).await.catch_return()?;
														match self.single().is_some() {
								false => {
																		let args = match f.args().first().unwrap() {
										Value::Param(v) => {
											v.compute(stk, ctx, opt, Some(doc)).await?
										}
										v => v.to_owned(),
									};
																		let expr: Vec<Value> = expr.try_into()?;
																		let args: Vec<Value> = args.try_into()?;
																		for (name, expr) in args.into_iter().zip(expr) {
																				let name = syn::idiom(&name.to_raw_string())?;
																				out.set(stk, ctx, opt, name.as_ref(), expr).await?
									}
								}
								true => out = expr,
							}
						}
												Value::Function(f) if f.name() == Some("type::field") => {
														let expr =
								expr.compute(stk, ctx, opt, Some(doc)).await.catch_return()?;
														match self.single().is_some() {
								false => {
																		let name = match f.args().first().unwrap() {
										Value::Param(v) => {
											v.compute(stk, ctx, opt, Some(doc)).await?
										}
										v => v.to_owned(),
									};
																											let name = if let Some(x) = alias.as_ref().map(Cow::Borrowed) {
										x
									} else {
										Cow::Owned(syn::idiom(&name.to_raw_string())?)
									};
																		out.set(stk, ctx, opt, name.as_ref(), expr).await?
								}
								true => out = expr,
							}
						}
												_ => {
							let expr =
								expr.compute(stk, ctx, opt, Some(doc)).await.catch_return()?;
														if self.single().is_some() {
								out = expr;
							} else {
								out.set(stk, ctx, opt, name.as_ref(), expr).await?;
							}
						}
					}
				}
			}
		}
		Ok(out)
	}
}
#[revisioned(revision = 1)]
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[non_exhaustive]
pub enum Field {
		#[default]
	All,
		Single {
		expr: Value,
				alias: Option<Idiom>,
	},
}
impl Display for Field {
	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
		match self {
			Self::All => f.write_char('*'),
			Self::Single {
				expr,
				alias,
			} => {
				Display::fmt(expr, f)?;
				if let Some(alias) = alias {
					f.write_str(" AS ")?;
					Display::fmt(alias, f)
				} else {
					Ok(())
				}
			}
		}
	}
}