// SPDX-FileCopyrightText: 2025 Slatian
//
// SPDX-License-Identifier: LGPL-3.0-only

use crate::rusqlite::AssemblyContext;
use crate::rusqlite::InvertableRusqliteQuery;
use crate::rusqlite::RusqliteQuery;

use crate::sql::Field;

/// Turns Criteria into query paramters for rusqlite.
/// Implement on your own datatypes to make them work with rusqlite.
///
/// If you need to pass through errors see [TryAssembleRusqliteQuery][crate::rusqlite::TryAssembleRusqliteQuery].
pub trait AssembleRusqliteQuery<F: Field, C> {
	/// Uses knowlege from the implementation and the prefix to assemble
	/// ready to use query parts.
	///
	/// Implementers: This is the only method you need to implement.
	///
	/// When knowlege from the outside world is needed one can use `user_context`
	/// for that, otherwise it should be an empty tuple `()`.
	///
	/// When calling as part of query construction passing an empty string
	/// as the prefix is okay and an intended usecase.
	///
	/// When calling from implementation to sub-implementation,
	/// crossing a non `1:1` join boundry in sql, the caller should
	/// append the reason for switching context to the prefix.
	///
	/// i.e. `prefix+"linked_from_"`
	///
	/// Also see: [`ToRusqliteSingleField::assemble_query()`]
	///
	/// [`ToRusqliteSingleField::assemble_query()`]:
	/// 	 crate::rusqlite::ToRusqliteSingleField::assemble_query
	fn assemble_rusqlite_query(
		&self,
		assembly_context: &AssemblyContext,
		user_context: &C,
	) -> InvertableRusqliteQuery<F>;

	/// Like `assemble_rusqlite_query`, but resolves any dangling
	/// inversion optimizations.
	///
	/// When to use this method:
	/// * When you kick off query assembly in your database code
	/// * When directly constructing `AND` and `OR` branches.
	///
	/// When not to use this method:
	/// * Whenever you are just passing on the result
	/// * When you are implementing inversion code.
	///
	/// Use `assemble_rusqlite_query` there instead.
	///
	fn assemble_finished_rusqlite_query(
		&self,
		assembly_context: &AssemblyContext,
		user_context: &C,
	) -> RusqliteQuery<F> {
		self.assemble_rusqlite_query(assembly_context, user_context)
			.get_corrected_query()
	}

	/// Stripped down version of the assembly function intended to be called
	/// from database code to get the query generation started.
	///
	/// Uses an empty prefix and path.
	fn assemble_rusqlite_query_for_db(&self, user_context: &C) -> RusqliteQuery<F> {
		self.assemble_finished_rusqlite_query(&AssemblyContext::new_root(), user_context)
	}
}
