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

use crate::search::CriteriumSearchChain;
use crate::search::Phrase;

/// The operater a search chain will use after it has been constructed.
#[derive(Clone, Debug)]
pub enum CriteriumSearchChainBuilderType {
	/// All of the sub-chains must match for the query to match.
	And,

	/// One of the sub-chains must match for the query to match
	Or,
}

/// Builder struct for a [CriteriumSearchChain][crate::search::CriteriumSearchChain].
///
/// Similar to [CriteriumChain][crate::chain::CriteriumChain].
#[derive(Clone, Debug)]
pub struct CriteriumSearchChainBuilder {
	/// The operator to use for the assembled chain.
	pub typ: CriteriumSearchChainBuilderType,

	/// Child statements that will end up in the final chain.
	pub children: Vec<CriteriumSearchChain>,
}

impl CriteriumSearchChainBuilder {
	/// Contructs a new `CriteriumSearchChainBuilder` using the given type.
	///
	/// Also see the `and()` and `or()` constructors.
	pub fn new(typ: CriteriumSearchChainBuilderType) -> Self {
		Self {
			typ: typ,
			children: Vec::new(),
		}
	}

	/// Convenience wrapper around `new()` constructing a chain with an `And` operator.
	pub fn and() -> Self {
		Self::new(CriteriumSearchChainBuilderType::And)
	}

	/// Convenience wrapper around `new()` constructing a chain with an `Or` operator.
	pub fn or() -> Self {
		Self::new(CriteriumSearchChainBuilderType::Or)
	}

	/// Adds a Phrase to the chain
	pub fn add_phrase(&mut self, phrase: Phrase) {
		self.children.push(CriteriumSearchChain::Phrase(phrase))
	}

	/// Adds a sub-chain
	pub fn add_chain(&mut self, chain: CriteriumSearchChain) {
		self.children.push(chain);
	}

	/// Constructs the `CriteriumSearchChain` consuming the builder.
	pub fn to_chain(self) -> CriteriumSearchChain {
		match self.typ {
			CriteriumSearchChainBuilderType::And => CriteriumSearchChain::And(self.children),
			CriteriumSearchChainBuilderType::Or => CriteriumSearchChain::Or(self.children),
		}
	}
}

impl From<CriteriumSearchChainBuilder> for CriteriumSearchChain {
	/// Uses the `to_chain()` method to convert a `CriteriumSearchChainBuilder`
	/// to a `CriteriumSearchChain`.
	fn from(builder: CriteriumSearchChainBuilder) -> CriteriumSearchChain {
		return builder.to_chain();
	}
}

impl TryFrom<CriteriumSearchChain> for CriteriumSearchChainBuilder {
	type Error = ();

	/// Converts `And`, `Or` and `PhraseChain` chains to a `CriteriumSearchChainBuilder`,
	/// returns an error for all other kinds of chain.
	fn try_from(chain: CriteriumSearchChain) -> Result<Self, ()> {
		match chain {
			CriteriumSearchChain::And(cs) => Ok(CriteriumSearchChainBuilder {
				typ: CriteriumSearchChainBuilderType::And,
				children: cs,
			}),
			CriteriumSearchChain::Or(cs) => Ok(CriteriumSearchChainBuilder {
				typ: CriteriumSearchChainBuilderType::Or,
				children: cs,
			}),
			_ => Err(()),
		}
	}
}
