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

use crate::chain::CriteriumChain;
use crate::direct_match::{DirectMatchResult, DirectMatchResultKind};
use crate::DirectMatch;

impl<T, V> DirectMatch<V> for CriteriumChain<T>
where
	T: DirectMatch<V>,
	V: ?Sized,
{
	type Output = T::Output;

	fn criterium_match(&self, data: &V) -> Self::Output {
		match self {
			CriteriumChain::NotChain(chain) => chain.criterium_match(data).invert(),
			CriteriumChain::Match(c) => c.criterium_match(data),
			CriteriumChain::Not(c) => c.criterium_match(data).invert(),
			CriteriumChain::WithLikelihood { matcher, .. } => matcher.criterium_match(data),
			CriteriumChain::MatchAlways => Self::Output::new(true),
			CriteriumChain::MatchNever => Self::Output::new(false),
			CriteriumChain::And(list) => {
				let mut output: Option<Self::Output> = None;
				let mut output_is_unknown = false;
				for c in &list.criteria {
					let res = c.criterium_match(data);
					match res.kind() {
						DirectMatchResultKind::False => return res,
						DirectMatchResultKind::True => {
							if output.is_none() {
								output = Some(res);
							}
						}
						DirectMatchResultKind::Unknown => {
							if !output_is_unknown {
								output = Some(res);
								output_is_unknown = true;
							}
						}
						DirectMatchResultKind::Error => return res,
					}
				}
				return output.unwrap_or(Self::Output::new(list.fallback));
			}
			CriteriumChain::Or(list) => {
				let mut output: Option<Self::Output> = None;
				let mut output_is_unknown = false;
				for c in &list.criteria {
					let res = c.criterium_match(data);
					match res.kind() {
						DirectMatchResultKind::True => return res,
						DirectMatchResultKind::False => {
							if output.is_none() {
								output = Some(res);
							}
						}
						DirectMatchResultKind::Unknown => {
							if !output_is_unknown {
								output = Some(res);
								output_is_unknown = true;
							}
						}
						DirectMatchResultKind::Error => return res,
					}
				}
				return output.unwrap_or(Self::Output::new(list.fallback));
			}
		}
	}
}
