use reblessive::tree::Stk;

use super::IgnoreError;
use crate::catalog::Record;
use crate::ctx::FrozenContext;
use crate::dbs::{Operable, Options, Processable, Statement, Workable};
use crate::doc::Document;
use crate::err::Error;
use crate::val::Value;

impl Document {
	#[cfg_attr(
		feature = "trace-doc-ops",
		instrument(level = "trace", name = "Document::process", skip_all)
	)]
	pub(crate) async fn process(
		stk: &mut Stk,
		ctx: &FrozenContext,
		opt: &Options,
		stm: &Statement<'_>,
		Processable {
			doc_ctx,
			record_strategy,
			generate,
			rid,
			val,
			ir,
		}: Processable,
	) -> Result<Value, IgnoreError> {
		// Check current context
		if ctx.is_done(None).await? {
			// Don't process the document
			return Err(IgnoreError::Ignore);
		}
		// Setup a new workable
		let ins = match val {
			Operable::Value(v) => (v, Workable::Normal),
			Operable::Insert(v, o) => (v, Workable::Insert(o)),
			Operable::Relate(f, v, w, o) => (v, Workable::Relate(f, w, o)),
			Operable::Count(count) => {
				(Record::new(Value::from(count).into()).into_read_only(), Workable::Normal)
			}
		};
		// Setup a new document
		let mut doc =
			Document::new(doc_ctx, rid, ir, generate, ins.0, ins.1, false, record_strategy);
		// Process the statement
		let res = match stm {
			Statement::Select {
				stmt,
				omit,
			} => doc.select(stk, ctx, opt, stmt, omit).await?,
			Statement::Create(_) => doc.create(stk, ctx, opt, stm).await?,
			Statement::Upsert(_) => doc.upsert(stk, ctx, opt, stm).await?,
			Statement::Update(_) => doc.update(stk, ctx, opt, stm).await?,
			Statement::Relate(_) => doc.relate(stk, ctx, opt, stm).await?,
			Statement::Delete(_) => doc.delete(stk, ctx, opt, stm).await?,
			Statement::Insert(stm) => doc.insert(stk, ctx, opt, stm).await?,
			stm => {
				return Err(IgnoreError::from(anyhow::Error::new(Error::unreachable(
					format_args!("Unexpected statement type: {stm:?}"),
				))));
			}
		};
		Ok(res)
	}
}
